2 * $Logfile: /Freespace2/code/Fred2/Sexp_tree.cpp $
7 * Sexp tree handler class. Almost everything is handled by this class.
10 * Revision 1.1 2002/05/03 03:28:08 root
14 * 43 9/07/99 9:22p Jefff
15 * added 2 more assignable medals
17 * 42 9/07/99 1:05a Andsager
18 * Added team-score sexp for multi team vs team missions
20 * 41 8/27/99 4:07p Andsager
21 * Add is-ship-visible sexp. Make ship-vanish sexp SINGLE player only
23 * 40 8/24/99 4:25p Andsager
24 * Add ship-vanish sexp
26 * 39 8/16/99 10:04p Andsager
27 * Add special-warp-dist and special-warpout-name sexp for Knossos device
30 * 38 8/09/99 2:00p Dave
33 * 37 8/02/99 4:26p Dave
34 * Added 2 new sexpressions.
36 * 36 8/02/99 1:43p Andsager
39 * 35 7/28/99 1:36p Andsager
40 * Modify cargo1 to include flag CARGO_NO_DEPLETE. Add sexp
41 * cargo-no-deplete (only for BIG / HUGE). Modify ship struct to pack
44 * 34 7/24/99 4:56p Dave
45 * Added 3 new sexpressions.
47 * 33 7/21/99 8:10p Dave
48 * First run of supernova effect.
50 * 32 7/20/99 9:19p Andsager
51 * Added facing waypoint sexp
53 * 31 7/20/99 9:54a Andsager
54 * Add subsys-set-random sexp
56 * 30 7/19/99 12:02p Andsager
57 * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
58 * only blow up subsystem if its strength is > 0
60 * 29 7/13/99 3:37p Andsager
61 * Add secondaries-depleted sexp
63 * 28 7/12/99 12:01p Andsager
64 * Make message by default come from command.
66 * 27 7/08/99 12:06p Andsager
67 * Add turret-tagged-only and turret-tagged-clear sexp.
69 * 26 6/29/99 10:08a Andsager
72 * 25 6/23/99 5:51p Andsager
73 * Add waypoint-cap-speed. Checkin stealth ai - inactive.
75 * 24 6/16/99 10:21a Dave
76 * Added send-message-list sexpression.
78 * 23 6/01/99 8:35p Dave
79 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
80 * awacs-set-radius sexpression.
82 * 22 5/24/99 11:28a Dave
83 * Sexpression for adding/removing ships from the hud escort list.
85 * 21 5/20/99 1:40p Andsager
86 * Fix find_text() to only look at nodes that are used.
88 * 20 5/04/99 5:21p Andsager
90 * 19 4/28/99 9:33a Andsager
91 * Add turret-free and turret-lock (and -all) sexp. Stargger start time
92 * of beam weapons beam-free and beam-free-all.
94 * 18 4/26/99 2:14p Andsager
95 * Add beam-protect-ship and beam-unprotect-ship sexp.
97 * 17 4/23/99 12:01p Johnson
100 * 16 4/02/99 9:54a Dave
101 * Added a few more options in the weapons.tbl for beam weapons. Attempt
102 * at putting "pain" packets into multiplayer.
104 * 15 3/20/99 3:46p Dave
105 * Added support for model-based background nebulae. Added 3 new
108 * 14 3/04/99 6:09p Dave
109 * Added in sexpressions for firing beams and checking for if a ship is
112 * 13 3/01/99 10:00a Dave
113 * Fxied several dogfight related stats bugs.
115 * 12 2/26/99 6:01p Andsager
116 * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
118 * 11 1/26/99 10:09a Andsager
119 * Better checking for modifying/deleting variables
121 * 10 1/25/99 5:16p Andsager
122 * Handle change of variable type on modify-variable
124 * 9 1/25/99 8:10a Andsager
125 * Add sexp_modify_variable(). Changed syntax checking to allow, adding
126 * operator return type ambiguous
128 * 8 1/24/99 11:37p Dave
129 * First full rev of beam weapons. Very customizable. Removed some bogus
130 * Int3()'s in low level net code.
132 * 7 1/20/99 9:02a Andsager
133 * Fix bug in verify_and_fix_arguments, where list of strings can have
136 * 6 1/19/99 3:57p Andsager
137 * Round 2 of variables
139 * 5 12/17/98 2:39p Andsager
140 * Added bitmaps for campaign editor. Changed input into insert() to
143 * 4 12/17/98 2:34p Andsager
144 * new bitmap and dialog for campaign editor
146 * 3 10/13/98 9:27a Dave
147 * Started neatening up freespace.h
149 * 2 10/07/98 6:28p Dave
150 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
151 * Fred. Globalized mission and campaign file extensions. Removed Silent
152 * Threat specific code.
154 * 1 10/07/98 3:02p Dave
156 * 1 10/07/98 3:00p Dave
158 * 179 9/25/98 1:33p Andsager
159 * Add color to event editor (root and chain) indicating mission directive
161 * 178 9/15/98 4:26p Allender
162 * added sexpression help for some sexpressions
164 * 177 6/09/98 5:15p Lawrance
165 * French/German localization
167 * 176 5/21/98 12:58a Hoffoss
168 * Fixed warnings optimized build turned up.
170 * 175 5/15/98 6:45p Hoffoss
171 * Made some things not appear in the release version of Fred.
173 * 174 5/14/98 10:15a Allender
174 * add optional argument to prevous-goal/event operators to specify what
175 * sexpression should return when being played as a single mission
177 * 173 5/04/98 10:57a Johnson
178 * Fixed bug with labeled roots allowing insert.
180 * 172 4/25/98 7:39p Allender
181 * fixd some small hotkey stuff. Worked on turret orientation being
182 * correct for multiplayer. new sexpression called end-campaign will will
183 * end the main campaign
185 * 171 4/23/98 5:49p Hoffoss
186 * Added tracking of techroom database list info in pilot files, added
187 * sexp to add more to list, made mouse usable on ship listing in tech
190 * 170 4/15/98 3:46p Hoffoss
191 * Fixed bug with getting a default argument value from an opf listing was
192 * utilizing temporary memory that was being destroyed before we were
195 * 169 4/14/98 5:46p Hoffoss
196 * Added special-check operator.
198 * 168 4/14/98 5:24p Hoffoss
199 * Added a custom operator for training handling for Mike K.
201 * 167 4/14/98 4:19p Jim
202 * Fixed bug with deleting an argument to an operator that you shouldn't
205 * 166 4/07/98 10:51a Allender
206 * remove any allied from message senders. Make heads for mission
207 * specific messages play appropriately
209 * 165 4/03/98 12:17a Allender
210 * new sexpression to detect departed or destroyed. optionally disallow
211 * support ships. Allow docking with escape pods
213 * 164 3/30/98 2:57p Hoffoss
214 * Fixed event listing in campaign editor mode.
216 * 163 3/26/98 3:13p Duncan
217 * Fixed bug in goal name listing generation function. Allender forgot
218 * about an assumption being made with them when he used it for
221 * 162 3/23/98 2:46p Hoffoss
222 * Fixed bug with default argument available for OPF_MESSAGE even when
223 * there were no messages, and added "#Command" as a message source to
226 * 161 3/21/98 7:36p Lawrance
227 * Move jump nodes to own lib.
234 #include "sexp_tree.h"
237 #include "management.h"
239 #include "operatorargtypeselect.h"
240 #include "linklist.h"
241 #include "eventeditor.h"
242 #include "missiongoalsdlg.h"
244 #include "missionmessage.h"
245 #include "missioncampaign.h"
246 #include "campaigneditordlg.h"
247 #include "hudsquadmsg.h"
248 #include "ignoreordersdlg.h"
250 #include "controlsconfig.h"
251 #include "hudgauges.h"
252 #include "starfield.h"
253 #include "jumpnode.h"
254 #include "addvariabledlg.h"
255 #include "modifyvariabledlg.h"
257 #define MAX_OP_MENUS 30
259 #define ID_VARIABLE_MENU 0xda00
260 #define ID_ADD_MENU 0xdc00
261 #define ID_REPLACE_MENU 0xde00
262 // note: stay below 0xe000 so we don't collide with MFC defines..
265 #define new DEBUG_NEW
267 static char THIS_FILE[] = __FILE__;
270 BEGIN_MESSAGE_MAP(sexp_tree, CTreeCtrl)
271 //{{AFX_MSG_MAP(sexp_tree)
272 ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag)
277 ON_NOTIFY_REFLECT(TVN_KEYDOWN, OnKeydown)
281 static int Add_count, Replace_count;
282 static int Modify_variable;
284 struct sexp_help_struct {
289 { OP_PLUS, "Plus (Arithmetic operator)\r\n"
290 "\tAdds numbers and returns results.\r\n\r\n"
291 "Returns a number; Takes 2 or more numeric arguments." },
293 { OP_MINUS, "Minus (Arithmetic operator)\r\n"
294 "\tSubtracts numbers and returns results.\r\n\r\n"
295 "Returns a number; Takes 2 or more numeric arguments." },
297 { OP_MOD, "Mod (Arithmetic operator)\r\n"
298 "\tDivides numbers and returns the remainer.\r\n\r\n"
299 "Returns a number; Takes 2 or more numeric arguments." },
301 { OP_MUL, "Multiply (Arithmetic operator)\r\n"
302 "\tMultiplies numbers and returns results.\r\n\r\n"
303 "Returns a number; Takes 2 or more numeric arguments." },
305 { OP_DIV, "Divide (Arithmetic operator)\r\n"
306 "\tDivides numbers and returns results.\r\n\r\n"
307 "Returns a number; Takes 2 or more numeric arguments." },
309 { OP_RAND, "Random number (Arithmetic operator)\r\n"
310 "\tGets a random number and returns result.\r\n\r\n"
311 "Returns a number; Takes 2 numeric arguments.\r\n"
312 "\t1:\tLow range of random number.\r\n"
313 "\t2:\tHigh range of random number." },
315 { OP_TRUE, "True (Boolean operator)\r\n"
316 "\tA true boolean state\r\n\r\n"
317 "Returns a boolean value." },
319 { OP_FALSE, "False (Boolean operator)\r\n"
320 "\tA false boolean state\r\n\r\n"
321 "Returns a boolean value." },
323 { OP_AND, "And (Boolean operator)\r\n"
324 "\tAnd is true if all of it's arguments are true.\r\n\r\n"
325 "Returns a boolean value; Takes 2 or more boolean arguments." },
327 { OP_OR, "Or (Boolean operator)\r\n"
328 "\tOr is true if any of it's arguments are true.\r\n\r\n"
329 "Returns a boolean value; Takes 2 or more boolean arguments." },
331 { OP_EQUALS, "Equals (Boolean operator)\r\n"
332 "\tIs true if all of it's arguments are equal.\r\n\r\n"
333 "Returns a boolean value; Takes 2 or more numeric arguments." },
335 { OP_GREATER_THAN, "Greater than (Boolean operator)\r\n"
336 "\tTrue if first argument is greater than the second argument.\r\n\r\n"
337 "Returns a boolean value; Takes 2 numeric arguments." },
339 { OP_LESS_THAN, "Less than (Boolean operator)\r\n"
340 "\tTrue if first argument is less than the second argument.\r\n\r\n"
341 "Returns a boolean value; Takes 2 numeric arguments." },
343 { OP_IS_IFF, "Is IFF (Boolean operator)\r\n"
344 "\tTrue if ship{s} are all of the specified team.\r\n\r\n"
345 "Returns a boolean value; Takes 2 or more arguments:\r\n"
346 "/t1:\tTeam (\"friendly\", \"hostile\" or \"unknown\").\r\n"
347 "\tRest:\tName of ship to check." },
349 { OP_HAS_TIME_ELAPSED, "Has time elapsed (Boolean operator)\r\n"
350 "\tBecomes true when the specified amount of time has elapsed (Mission time "
351 "becomes greater than the specified time).\r\n"
352 "Returns a boolean value; Takes 1 numeric argument:\r\n"
353 "\t1:\tThe amount of time in seconds." },
355 { OP_NOT, "Not (Boolean operator)\r\n"
356 "\tReturns opposite boolean value of argument (True becomes false, and "
357 "false becomes true).\r\n\r\n"
358 "Returns a boolean value; Takes 1 boolean argument." },
360 { OP_PREVIOUS_GOAL_TRUE, "Previous Mission Goal True (Boolean operator)\r\n"
361 "\tReturns true if the specified goal in the specified mission is true "
362 "(or succeeded). It returns false otherwise.\r\n\r\n"
363 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
364 "\t1:\tName of the mission.\r\n"
365 "\t2:\tName of the goal in the mission.\r\n"
366 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
367 "this mission is played as a single mission." },
369 { OP_PREVIOUS_GOAL_FALSE, "Previous Mission Goal False (Boolean operator)\r\n"
370 "\tReturns true if the specified goal in the specified mission "
371 "is false (or failed). It returns false otherwise.\r\n\r\n"
372 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
373 "\t1:\tName of the mission.\r\n"
374 "\t2:\tName of the goal in the mission.\r\n"
375 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
376 "this mission is played as a single mission." },
378 { OP_PREVIOUS_GOAL_INCOMPLETE, "Previous Mission Goal Incomplete (Boolean operator)\r\n"
379 "\tReturns true if the specified goal in the specified mission "
380 "is incomplete (not true or false). It returns false otherwise.\r\n\r\n"
381 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
382 "\t1:\tName of the mission.\r\n"
383 "\t2:\tName of the goal in the mission.\r\n"
384 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
385 "this mission is played as a single mission." },
387 { OP_PREVIOUS_EVENT_TRUE, "Previous Mission Event True (Boolean operator)\r\n"
388 "\tReturns true if the specified event in the specified mission is true "
389 "(or succeeded). It returns false otherwise.\r\n\r\n"
390 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
391 "\t1:\tName of the mission.\r\n"
392 "\t2:\tName of the event in the mission.\r\n"
393 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
394 "this mission is played as a single mission." },
396 { OP_PREVIOUS_EVENT_FALSE, "Previous Mission Event False (Boolean operator)\r\n"
397 "\tReturns true if the specified event in the specified mission "
398 "is false (or failed). It returns false otherwise.\r\n\r\n"
399 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
400 "\t1:\tName of the mission.\r\n"
401 "\t2:\tName of the event in the mission.\r\n"
402 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
403 "this mission is played as a single mission." },
405 { OP_PREVIOUS_EVENT_INCOMPLETE, "Previous Mission Event Incomplete (Boolean operator)\r\n"
406 "\tReturns true if the specified event in the specified mission "
407 "is incomplete (not true or false). It returns false otherwise.\r\n\r\n"
408 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
409 "\t1:\tName of the mission.\r\n"
410 "\t2:\tName of the event in the mission.\r\n"
411 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
412 "this mission is played as a single mission." },
414 { OP_GOAL_TRUE_DELAY, "Mission Goal True (Boolean operator)\r\n"
415 "\tReturns true N seconds after the specified goal in the this mission is true "
416 "(or succeeded). It returns false otherwise.\r\n\r\n"
417 "Returns a boolean value; Takes 2 arguments:\r\n"
418 "\t1:\tName of the event in the mission.\r\n"
419 "\t2:\tNumber of seconds to delay before returning true."},
421 { OP_GOAL_FALSE_DELAY, "Mission Goal False (Boolean operator)\r\n"
422 "\tReturns true N seconds after the specified goal in the this mission is false "
423 "(or failed). It returns false otherwise.\r\n\r\n"
424 "Returns a boolean value; Takes 2 arguments:\r\n"
425 "\t1:\tName of the event in the mission.\r\n"
426 "\t2:\tNumber of seconds to delay before returning true."},
428 { OP_GOAL_INCOMPLETE, "Mission Goal Incomplete (Boolean operator)\r\n"
429 "\tReturns true if the specified goal in the this mission is incomplete. This "
430 "sexpression will only be useful in conjunction with another sexpression like"
431 "has-time-elapsed. Used alone, it will return true upon misison startup."
432 "Returns a boolean value; Takes 1 argument:\r\n"
433 "\t1:\tName of the event in the mission."},
435 { OP_EVENT_TRUE_DELAY, "Mission Event True (Boolean operator)\r\n"
436 "\tReturns true N seconds after the specified event in the this mission is true "
437 "(or succeeded). It returns false otherwise.\r\n\r\n"
438 "Returns a boolean value; Takes 2 arguments:\r\n"
439 "\t1:\tName of the event in the mission.\r\n"
440 "\t2:\tNumber of seconds to delay before returning true."},
442 { OP_EVENT_FALSE_DELAY, "Mission Event False (Boolean operator)\r\n"
443 "\tReturns true N seconds after the specified event in the this mission is false "
444 "(or failed). It returns false otherwise.\r\n\r\n"
445 "Returns a boolean value; Takes 2 arguments:\r\n"
446 "\t1:\tName of the event in the mission.\r\n"
447 "\t2:\tNumber of seconds to delay before returning true."},
449 { OP_EVENT_INCOMPLETE, "Mission Event Incomplete (Boolean operator)\r\n"
450 "\tReturns true if the specified event in the this mission is incomplete. This "
451 "sexpression will only be useful in conjunction with another sexpression like"
452 "has-time-elapsed. Used alone, it will return true upon misison startup."
453 "Returns a boolean value; Takes 1 argument:\r\n"
454 "\t1:\tName of the event in the mission."},
456 { OP_IS_DESTROYED_DELAY, "Is destroyed delay (Boolean operator)\r\n"
457 "\tBecomes true <delay> seconds after all specified ships have been destroyed.\r\n\r\n"
458 "Returns a boolean value; Takes 2 or more arguments:\r\n"
459 "\t1:\tTime delay in seconds (see above).\r\n"
460 "\tRest:\tName of ship (or wing) to check status of." },
462 { OP_IS_SUBSYSTEM_DESTROYED_DELAY, "Is subsystem destroyed delay (Boolean operator)\r\n"
463 "\tBecomes true <delay> seconds after the specified subsystem of the specified "
464 "ship is destroyed.\r\n\r\n"
465 "Returns a boolean value; Takes 3 arguments:\r\n"
466 "\t1:\tName of ship the subsystem we are checking is on.\r\n"
467 "\t2:\tThe name of the subsystem we are checking status of.\r\n"
468 "\t3:\tTime delay in seconds (see above)." },
470 { OP_IS_DISABLED_DELAY, "Is disabled delay (Boolean operator)\r\n"
471 "\tBecomes true <delay> seconds after the specified ship(s) are disabled. A "
472 "ship is disabled when all of it's engine subsystems are destroyed. All "
473 "ships must be diabled for this function to return true.\r\n\r\n"
474 "Returns a boolean value; Takes 2 or more arguments:\r\n"
475 "\t1:\tTime delay is seconds (see above).\r\n"
476 "\tRest:\tNames of ships to check disabled status of." },
478 { OP_IS_DISARMED_DELAY, "Is disarmed delay (Boolean operator)\r\n"
479 "\tBecomes true <delay> seconds after the specified ship(s) are disarmed. A "
480 "ship is disarmed when all of it's turret subsystems are destroyed. All "
481 "ships must be disarmed for this function to return true.\r\n\r\n"
482 "Returns a boolean value; Takes 2 or more arguments:\r\n"
483 "\t1:\tTime delay is seconds (see above).\r\n"
484 "\tRest:\tNames of ships to check disarmed status of." },
486 { OP_HAS_DOCKED_DELAY, "Has docked delay (Boolean operator)\r\n"
487 "\tBecomes true <delay> seconds after the specified ships have docked the "
488 "specified number of times.\r\n\r\n"
489 "Returns a boolean value; Takes 4 arguments:\r\n"
490 "\t1:\tThe name of the docker ship\r\n"
491 "\t2:\tThe name of the dockee ship\r\n"
492 "\t3:\tThe number of times they have to have docked\r\n"
493 "\t4:\tTime delay in seconds (see above)." },
495 { OP_HAS_UNDOCKED_DELAY, "Has undocked delay (Boolean operator)\r\n"
496 "\tBecomes true <delay> seconds after the specified ships have undocked the "
497 "specified number of times.\r\n\r\n"
498 "Returns a boolean value; Takes 4 arguments:\r\n"
499 "\t1:\tThe name of the docker ship\r\n"
500 "\t2:\tThe name of the dockee ship\r\n"
501 "\t3:\tThe number of times they have to have undocked\r\n"
502 "\t4:\tTime delay in seconds (see above)." },
504 { OP_HAS_ARRIVED_DELAY, "Has arrived delay (Boolean operator)\r\n"
505 "\tBecomes true <delay> seconds after the specified ship(s) have arrived into the mission\r\n\r\n"
506 "Returns a boolean value; Takes 2 or more arguments:\r\n"
507 "\t1:\tTime delay in seconds (see above).\r\n"
508 "\tRest:\tName of ship (or wing) we want to check has arrived." },
510 { OP_HAS_DEPARTED_DELAY, "Has departed delay (Boolean operator)\r\n"
511 "\tBecomes true <delay> seconds after the specified ship(s) or wing(s) have departed "
512 "from the mission by warping out. If any ship was destroyed, this operator will "
513 "never be true.\r\n\r\n"
514 "Returns a boolean value; Takes 2 or more arguments:\r\n"
515 "\t1:\tTime delay in seconds (see above).\r\n"
516 "\tRest:\tName of ship (or wing) we want to check has departed." },
518 { OP_WAYPOINTS_DONE_DELAY, "Waypoints done delay (Boolean operator)\r\n"
519 "\tBecomes true <delay> seconds after the specified ship has completed flying the "
520 "specified waypoint path.\r\n\r\n"
521 "Returns a boolean value; Takes 3 arguments:\r\n"
522 "\t1:\tName of ship we are checking.\r\n"
523 "\t2:\tWaypoint path we want to check if ship has flown.\r\n"
524 "\t3:\tTime delay in seconds (see above)." },
526 { OP_SHIP_TYPE_DESTROYED, "Ship Type Destroyed (Boolean operator)\r\n"
527 "\tBecomes true when the specified percentage of ship types in this mission "
528 "have been destroyed. The ship type is a generic type such as fighter/bomber, "
529 "transport, etc. Fighters and bombers count as the same type.\r\n\r\n"
530 "Returns a boolean value; Takes 2 arguments:\r\n"
531 "\t1:\tPercentage of ships that must be destroyed.\r\n"
532 "\t2:\tShip type to check for." },
534 { OP_TIME_SHIP_DESTROYED, "Time ship destroyed (Time operator)\r\n"
535 "\tReturns the time the specified ship was destroy.\r\n\r\n"
536 "Returns a numeric value; Takes 1 argument:\r\n"
537 "\t1:\tName of ship we want to check." },
539 { OP_TIME_SHIP_ARRIVED, "Time ship arrived (Time operator)\r\n"
540 "\tReturns the time the specified ship arrived into the mission.\r\n\r\n"
541 "Returns a numeric value; Takes 1 argument:\r\n"
542 "\t1:\tName of ship we want to check." },
544 { OP_TIME_SHIP_DEPARTED, "Time ship departed (Time operator)\r\n"
545 "\tReturns the time the specified ship departed the mission by warping out. Being "
546 "destroyed doesn't count departed.\r\n\r\n"
547 "Returns a numeric value; Takes 1 argument:\r\n"
548 "\t1:\tName of ship we want to check." },
550 { OP_TIME_WING_DESTROYED, "Time wing destroyed (Time operator)\r\n"
551 "\tReturns the time the specified wing was destroy.\r\n\r\n"
552 "Returns a numeric value; Takes 1 argument:\r\n"
553 "\t1:\tName of wing we want to check." },
555 { OP_TIME_WING_ARRIVED, "Time wing arrived (Time operator)\r\n"
556 "\tReturns the time the specified wing arrived into the mission.\r\n\r\n"
557 "Returns a numeric value; Takes 1 argument:\r\n"
558 "\t1:\tName of wing we want to check." },
560 { OP_TIME_WING_DEPARTED, "Time wing departed (Time operator)\r\n"
561 "\tReturns the time the specified wing departed the mission by warping out. All "
562 "ships in the wing have to have warped out. If any are destroyed, the wing can "
563 "never be considered departed.\r\n\r\n"
564 "Returns a numeric value; Takes 1 argument:\r\n"
565 "\t1:\tName of ship we want to check." },
567 { OP_MISSION_TIME, "Mission time (Time operator)\r\n"
568 "\tReturns the current time into the mission.\r\n\r\n"
569 "Returns a numeric value." },
571 { OP_TIME_DOCKED, "Time docked (Time operator)\r\n"
572 "\tReturns the time the specified ships docked.\r\n\r\n"
573 "Returns a numeric value; Takes 3 arguments:\r\n"
574 "\t1:\tThe name of the docker ship.\r\n"
575 "\t2:\tThe name of the dockee ship.\r\n"
576 "\t3:\tThe number of times they must have docked to be true." },
578 { OP_TIME_UNDOCKED, "Time undocked (Time operator)\r\n"
579 "\tReturns the time the specified ships undocked.\r\n\r\n"
580 "Returns a numeric value; Takes 3 arguments:\r\n"
581 "\t1:\tThe name of the docker ship.\r\n"
582 "\t2:\tThe name of the dockee ship.\r\n"
583 "\t3:\tThe number of times they must have undocked to be true." },
585 { OP_SHIELDS_LEFT, "Sheilds left (Status operator)\r\n"
586 "\tReturns the current level of the specified ship's shields as a percentage.\r\n\r\n"
587 "Returns a numeric value; Takes 1 argument:\r\n"
588 "\t1:\tName of ship to check." },
590 { OP_HITS_LEFT, "Hits left (Status operator)\r\n"
591 "\tReturns the current level of the specified ship's hull as a percentage.\r\n\r\n"
592 "Returns a numeric value; Takes 1 argument:\r\n"
593 "\t1:\tName of ship to check." },
595 { OP_HITS_LEFT_SUBSYSTEM, "Hits left subsystem (Status operator)\r\n"
596 "\tReturns the current level of the specified ship's subsystem integrity as a percentage.\r\n\r\n"
597 "Returns a numeric value; Takes 1 argument:\r\n"
598 "\t1:\tName of ship to check.\r\n"
599 "\t2:\tName of subsystem on ship to check." },
601 { OP_DISTANCE, "Distance (Misc. Operator)\r\n"
602 "\tReturns the distance between 2 objects. These objects can be either a ship, "
603 "a wing, or a waypoint.\r\n\r\n"
604 "Returns a numeric value; Takes 2 arguments:\r\n"
605 "\t1:\tThe name of one of the objects.\r\n"
606 "\t2:\tThe name of the other object." },
608 { OP_LAST_ORDER_TIME, "Last order time (Status operator)\r\n"
609 "\tReturns true if <count> seconds have elapsed since one or more ships have received "
610 "a meaningful order from the player. A meaningful order is currently any order that "
611 "is not the warp out order.\r\n\r\n"
612 "Returns a boolean value; Takes 2 or more arguments:\r\n"
613 "\t1:\tTime in seconds that must elapse.\r\n"
614 "\tRest:\tName of ship or wing to check for having received orders." },
616 { OP_WHEN, "When (Conditional operator)\r\n"
617 "\tPerforms specified actions when a condition becomes true\r\n\r\n"
618 "Takes 2 or more arguments:\r\n"
619 "\t1:\tBoolean expression that must be true for actions to take place.\r\n"
620 "\tRest:\tActions to take when boolean expression becomes true." },
624 { OP_CHANGE_IFF, "Change IFF (Action operator)\r\n"
625 "\tSets the specified ship(s) to the specified team.\r\n"
626 "Takes 2 or more arguments:\r\n"
627 "\t1:\tTeam to change to (\"friendly\", \"hostile\" or \"unknown\").\r\n"
628 "\tRest:\tName of ship to change team status of." },
630 { OP_MODIFY_VARIABLE, "Modify-variable (Misc. operator)\r\n"
631 "\tModifies variable to specified value\r\n\r\n"
632 "Takes 2 arguments:\r\n"
633 "\t1:\tName of Variable.\r\n"
634 "\t2:\tValue to be set." },
636 { OP_PROTECT_SHIP, "Protect ship (Action operator)\r\n"
637 "\tProtects a ship from being attacked by any enemy ship. Any ship"
638 "that is protected will not come under enemy fire.\r\n\r\n"
639 "Takes 1 or more arguments:\r\n"
640 "\tAll:\tName of ship(s) to protect." },
642 { OP_UNPROTECT_SHIP, "Unprotect ship (Action operator)\r\n"
643 "\tUnprotects a ship from being attacked by any enemy ship. Any ship"
644 "that is not protected can come under enemy fire. This function is the opposite"
645 "of protect-ship.\r\n\r\n"
646 "Takes 1 or more arguments:\r\n"
647 "\tAll:\tName of ship(s) to protect." },
649 { OP_BEAM_PROTECT_SHIP, "Beam Protect ship (Action operator)\r\n"
650 "\tProtects a ship from being attacked with beam weapon. Any ship"
651 "that is beam protected will not come under enemy beam fire.\r\n\r\n"
652 "Takes 1 or more arguments:\r\n"
653 "\tAll:\tName of ship(s) to protect." },
655 { OP_BEAM_UNPROTECT_SHIP, "Beam Unprotect ship (Action operator)\r\n"
656 "\tUnprotects a ship from being attacked with beam weapon. Any ship"
657 "that is not beam protected can come under enemy beam fire. This function is the opposite"
658 "of beam-protect-ship.\r\n\r\n"
659 "Takes 1 or more arguments:\r\n"
660 "\tAll:\tName of ship(s) to protect." },
662 { OP_SEND_MESSAGE, "Send message (Action operator)\r\n"
663 "\tSends a message to the player. Can be send by a ship, wing, or special "
664 "source. To send it from a special source, make the first character of the first "
665 "argument a \"#\".\r\n\r\n"
666 "Takes 3 arguments:\r\n"
667 "\t1:\tName of who the message is from.\r\n"
668 "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\").\r\n"
669 "\t3:\tName of message (from message editor)." },
671 { OP_SELF_DESTRUCT, "Self destruct (Action operator)\r\n"
672 "\tCauses the specified ship(s) to self destruct.\r\n\r\n"
673 "Takes 1 or more arguments:\r\n"
674 "\tAll:\tName of ship to self destruct." },
676 { OP_NEXT_MISSION, "Next Mission (Action operator)\r\n"
677 "\tThe next mission operator is used for campaign branching in the campaign editor. "
678 "It specifies which mission should played be next in the campaign. This operator "
679 "generally follows a 'when' or 'cond' statment in the campaign file.\r\n\r\n"
680 "Takes 1 argument:\r\n"
681 "\t1:\tName of mission (filename) to proceed to." },
683 { OP_CLEAR_GOALS, "Clear goals (Action operator)\r\n"
684 "\tClears the goals for the specified ships and/or wings.\r\n\r\n"
685 "Takes 1 or more arguments:\r\n"
686 "\tAll:\tName of ship or wing." },
688 { OP_ADD_GOAL, "Add goal (Action operator)\r\n"
689 "\tAdds a goal to a ship or wing.\r\n\r\n"
690 "Takes 2 arguments:\r\n"
691 "\t1:\tName of ship or wing to all goal to.\r\n"
692 "\t2:\tGoal to add." },
694 { OP_SABOTAGE_SUBSYSTEM, "Sabotage subystem (Action operator)\r\n"
695 "\tReduces the specified subsystem integrity by the specified percentage."
696 "If the percntage strength of the subsystem (after completion) is less than 0%,"
697 "subsystem strength is set to 0%.\r\n\r\n"
698 "Takes 3 arguments:\r\n"
699 "\t1:\tName of ship subsystem is on.\r\n"
700 "\t2:\tName of subsystem to sabotage.\r\n"
701 "\t3:\tPercentage to reduce subsystem integrity by." },
703 { OP_REPAIR_SUBSYSTEM, "Repair Subystem (Action operator)\r\n"
704 "\tIncreases the specified subsystem integrity by the specified percentage."
705 "If the percntage strength of the subsystem (after completion) is greater than 100%,"
706 "subsystem strength is set to 100%.\r\n\r\n"
707 "Takes 3 arguments:\r\n"
708 "\t1:\tName of ship subsystem is on.\r\n"
709 "\t2:\tName of subsystem to repair.\r\n"
710 "\t3:\tPercentage to increase subsystem integrity by." },
712 { OP_SET_SUBSYSTEM_STRNGTH, "Set Subsystem Strength (Action operator)\r\n"
713 "\tSets the specified subsystem to the the specified percentage."
714 "If the percentage specified is < 0, strength is set to 0. If the percentage is "
715 "> 100 % the subsystem strength is set to 100%.\r\n\r\n"
716 "Takes 3 arguments:\r\n"
717 "\t1:\tName of ship subsystem is on.\r\n"
718 "\t2:\tName of subsystem to set strength.\r\n"
719 "\t3:\tPercentage to set subsystem integrity to." },
721 { OP_INVALIDATE_GOAL, "Invalidate goal (Action operator)\r\n"
722 "\tMakes a mission goal invalid, which causes it to now show up on mission goals "
723 "screen, or be evaluated.\r\n"
724 "Takes 1 or more arguments:\r\n"
725 "\tAll:\tName of mission goal to invalidate." },
727 { OP_VALIDATE_GOAL, "Validate goal (Action operator)\r\n"
728 "\tMakes a mission goal valid again, so it shows up on mission goals screen.\r\n"
729 "Takes 1 or more arguments:\r\n"
730 "\tAll:\tName of mission goal to validate." },
732 { OP_SEND_RANDOM_MESSAGE, "Send random message (Action operator)\r\n"
733 "\tSends a random message to the player from those supplied. Can be send by a "
734 "ship, wing, or special source. To send it from a special source, make the first "
735 "character of the first argument a \"#\".\r\n\r\n"
736 "Takes 3 or more arguments:\r\n"
737 "\t1:\tName of who the message is from.\r\n"
738 "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\")."
739 "\tRest:\tName of message (from message editor)." },
741 { OP_TRANSFER_CARGO, "Transfer Cargo (Action operator)\r\n"
742 "\tTransfers the cargo from one ship to another ship.\r\n\r\n"
743 "Takes 2 arguments:\r\n"
744 "\t1:\tName of ship that cargo is being transferred from.\r\n"
745 "\t2:\tName of ship that cargo is being transferred to." },
747 { OP_EXCHANGE_CARGO, "Exchange Cargo (Action operator)"
748 "\tExchanges the cargos of two ships. If one of the two ships contains no cargo, "
749 "the cargo is transferred instead.\r\n"
750 "Takes 2 arguments:\r\n"
751 "\t1:\tName of one of the ships.\r\n"
752 "\t2:\tName of the other ship." },
754 { OP_INT3, "Error (Debug directive)\r\n"
755 "Causes the game to halt with an error." },
757 { OP_AI_CHASE, "Ai-chase (Ship goal)\r\n"
758 "\tCauses the specified ship to chase and attack the specified target.\r\n\r\n"
759 "Takes 2 arguments:\r\n"
760 "\t1:\tName of ship to chase.\r\n"
761 "\t2:\tGoal priority (number between 0 and 89)." },
763 { OP_AI_DOCK, "Ai-dock (Ship goal)\r\n"
764 "\tCauses one ship to dock with another ship.\r\n\r\n"
765 "Takes 4 arguments:\r\n"
766 "\t1:\tName of dockee ship (The ship that \"docker\" will dock with).\r\n"
767 "\t2:\tDocker's docking point - Which dock point docker uses to dock.\r\n"
768 "\t3:\tDockee's docking point - Which dock point on dockee docker will move to.\r\n"
769 "\t4:\tGoal priority (number between 0 and 89)." },
771 { OP_AI_UNDOCK, "Ai-undock (Ship goal)\r\n"
772 "\tCauses the specified ship to undock from who it is currently docked with.\r\n\r\n"
773 "Takes 1 arguments:\r\n"
774 "\t1:\tGoal priority (number between 0 and 89)." },
776 { OP_AI_WARP_OUT, "Ai-warp-out (Ship/Wing Goal)\r\n"
777 "\tCauses the specified ship/wing to warp out of the mission. Currently, the ship will "
778 "warp out at it's current location. This behavior will change. Currently, the first "
779 "argument means nothing.\r\n\r\n"
780 "Takes 2 arguments:\r\n"
781 "\t1:\tName of waypoint path to follow to warp out (not used).\r\n"
782 "\t2:\tGoal priority (number between 0 and 89)." },
784 { OP_AI_WAYPOINTS, "Ai-waypoints (Ship goal)\r\n"
785 "\tCauses the specified ship to fly a waypoint path continuously.\r\n\r\n"
786 "Takes 2 arguments:\r\n"
787 "\t1:\tName of waypoint path to fly.\r\n"
788 "\t2:\tGoal priority (number between 0 and 89)." },
790 { OP_AI_WAYPOINTS_ONCE, "Ai-waypoints once (Ship goal)\r\n"
791 "\tCauses the specified ship to fly a waypoint path.\r\n\r\n"
792 "Takes 2 arguments:\r\n"
793 "\t1:\tName of waypoint path to fly.\r\n"
794 "\t2:\tGoal priority (number between 0 and 89)." },
796 { OP_AI_DESTROY_SUBSYS, "Ai-destroy subsys (Ship goal)\r\n"
797 "\tCauses the specified ship to attack and try and destroy the specified subsystem "
798 "on the specified ship.\r\n\r\n"
799 "Takes 3 arguments:\r\n"
800 "\t1:\tName of ship subsystem is on.\r\n"
801 "\t2:\tName of subsystem on the ship to attack and destroy.\r\n"
802 "\t3:\tGoal priority (number between 0 and 89)." },
804 { OP_AI_CHASE_WING, "Ai-chase wing (Ship goal)\r\n"
805 "\tCauses the specified ship to chase and attack the specified target.\r\n\r\n"
806 "Takes 2 arguments:\r\n"
807 "\t1:\tName of wing to chase.\r\n"
808 "\t2:\tGoal priority (number between 0 and 89)." },
810 { OP_AI_DISABLE_SHIP, "Ai-disable-ship (Ship/wing goal)\r\n"
811 "\tThis AI goal causes a ship/wing to destroy all of the engine subsystems on "
812 "the specified ship. This goal is different than ai-destroy-subsystem since a ship "
813 "may have multiple engine subsystems requiring the use of > 1 ai-destroy-subsystem "
815 "Takes 2 arguments:\r\n"
816 "\t1:\tName of ship whose engine subsystems should be destroyed\r\n"
817 "\t2:\tGoal priority (number between 0 and 89)." },
819 { OP_AI_DISARM_SHIP, "Ai-disarm-ship (Ship/wing goal)\r\n"
820 "\tThis AI goal causes a ship/wing to destroy all of the turret subsystems on "
821 "the specified ship. This goal is different than ai-destroy-subsystem since a ship "
822 "may have multiple turret subsystems requiring the use of > 1 ai-destroy-subsystem "
824 "Takes 2 arguments:\r\n"
825 "\t1:\tName of ship whose turret subsystems should be destroyed\r\n"
826 "\t2:\tGoal priority (number between 0 and 89)." },
828 { OP_AI_GUARD, "Ai-guard (Ship goal)\r\n"
829 "\tCauses the specified ship to guard a ship from other ships not on the same team.\r\n\r\n"
830 "Takes 2 arguments:\r\n"
831 "\t1:\tName of ship to guard.\r\n"
832 "\t2:\tGoal priority (number between 0 and 89)." },
834 { OP_AI_CHASE_ANY, "Ai-chase any (Ship goal)\r\n"
835 "\tCauses the specified ship to chase and attack any ship on the opposite team.\r\n\r\n"
836 "Takes 1 arguments:\r\n"
837 "\t1:\tGoal priority (number between 0 and 89)." },
839 { OP_AI_GUARD_WING, "Ai-guard wing (Ship goal)\r\n"
840 "\tCauses the specified ship to guard a wing of ships from other ships not on the "
842 "Takes 2 arguments:\r\n"
843 "\t1:\tName of wing to guard.\r\n"
844 "\t2:\tGoal priority (number between 0 and 89)." },
846 { OP_NOP, "Do-nothing (Action operator)\r\n"
847 "\tDoes nothing. This is used as the default for any required action arguments "
850 { OP_KEY_PRESSED, "Key-pressed (Boolean training operator)\r\n"
851 "\tBecomes true when the specified default key has been pressed. Default key "
852 "refers to the what the key normally is when not remapped. FreeSpace will "
853 "automatically account for any keys that have been remapped. If the optional "
854 "delay is specified, becomes true that many seconds after the key has been pressed.\r\n\r\n"
855 "Returns a boolean value; Takes 1 or 2 arguments:\r\n"
856 "\t1:\tDefault key to check for.\r\n"
857 "\t2:\tDelay before operator registers as true (optional).\r\n" },
859 { OP_KEY_RESET, "Key-reset (Training operator)\r\n"
860 "\tMarks the specified default key as having not been pressed, so key-pressed will be false "
861 "until the player presses it again. See key-pressed help for more information about "
862 "what a default key is.\r\n\r\n"
863 "Returns a boolean value; Takes 1 argument:\r\n"
864 "\t1:\tDefault key to reset." },
866 { OP_TARGETED, "Targeted (Boolean training operator)\r\n"
867 "\tIs true as long as the player has the specified ship (or ship's subsystem) targeted, "
868 "or has been targeted for the specified amount of time.\r\n\r\n"
869 "Returns a boolean value; Takes 1 to 3 arguments (first required, rest optional):\r\n"
870 "\t1:\tName of ship to check if targeted by player.\r\n"
871 "\t2:\tLength of time target should have been kept for (optional).\r\n"
872 "\t3:\tName of subsystem on ship to check if targeted (optional)." },
874 { OP_SPEED, "Speed (Boolean training operator)\r\n"
875 "\tBecomes true when the player has been within the specified speed range set by "
876 "set-training-context-speed for the specified amount of time.\r\n\r\n"
877 "Returns a boolean value; Takes 1 argument:\r\n"
878 "\t1:\tTime in seconds." },
880 { OP_FACING, "Facing (Boolean training operator)\r\n"
881 "\tIs true as long as the specified ship is within the player's specified "
882 "forward cone. A forward cone is defined as any point that the angle between the "
883 "vector of the point and the player, and the forward facing vector is within the "
884 "given angle.\r\n\r\n"
885 "Returns a boolean value; Takes 2 argument:\r\n"
886 "\t1:\tShip to check is withing forward cone.\r\n"
887 "\t2:\tAngle in degrees of the forward cone." },
889 { OP_FACING2, "Facing Waypoint(Boolean training operator)\r\n"
890 "\tIs true as long as the specified first waypoint is within the player's specified "
891 "forward cone. A forward cone is defined as any point that the angle between the "
892 "vector of the point and the player, and the forward facing vector is within the "
893 "given angle.\r\n\r\n"
894 "Returns a boolean value; Takes 2 argument:\r\n"
895 "\t1:\tName of waypoint path whose first point is withing forward cone.\r\n"
896 "\t2:\tAngle in degrees of the forward cone." },
898 { OP_ORDER, "Order (Boolean training operator)\r\n"
899 "\tBecomes true when the player had given the specified ship or wing the specified order.\r\n\r\n"
900 "Returns a boolean value; Takes 2 arguments:\r\n"
901 "\t1:\tName of ship or wing to check if given order to.\r\n"
902 "\t2:\tName of order to check if player has given." },
904 { OP_WAYPOINT_MISSED, "Waypoint-missed (Boolean training operator)\r\n"
905 "\tBecomes true when a waypoint is flown, but the waypoint is ahead of the one "
906 "they are supposed to be flying. The one they are supposed to be flying is the "
907 "next one in sequence in the path after the last one they have hit.\r\n\r\n"
908 "Returns a boolean value; Takes no arguments." },
910 { OP_PATH_FLOWN, "Path-flown (Boolean training operator)\r\n"
911 "\tBecomes true when all the waypoints in the path have been flown, in sequence.\r\n\r\n"
912 "Returns a boolean value; Takes no arguments." },
914 { OP_WAYPOINT_TWICE, "Waypoint-twice (Boolean training operator)\r\n"
915 "\tBecomes true when a waypoint is hit that is before the last one hit, which "
916 "indicates they have flown a waypoint twice.\r\n\r\n"
917 "Returns a boolean value; Takes no arguments." },
919 { OP_TRAINING_MSG, "Training-msg (Action training operator)\r\n"
920 "\tSends the player a training message. Uses the same messages as normal messages, "
921 "only they get displayed differently using this operator. If a secondary message "
922 "is specified, it is sent the last time, while the primary message is sent all other "
923 "times (event should have a repeat count greater than 1).\r\n\r\n"
924 "Takes 1-3 arguments:\r\n"
925 "\t1:\tName of primary message to send.\r\n"
926 "\t2:\tName of secondary message to send (optional).\r\n"
927 "\t3:\tDelay (in seconds) to wait before sending message. (optional)\r\n"
928 "\t4:\tAmount of Time (in seconds) to display message (optional)." },
930 { OP_SET_TRAINING_CONTEXT_FLY_PATH, "Set-training-context-fly-path (Training context operator)\r\n"
931 "\tTells FreeSpace that the player is expected to fly a waypoint path. This must be "
932 "executed before waypoint-missed, waypoint-twice and path-flown operators become valid.\r\n\r\n"
933 "Takes 2 arguments:\r\n"
934 "\t1:\tName of waypoint path player should fly.\r\n"
935 "\t2:\tDistance away a player needs to be from a waypoint for it to be registered as flown." },
937 { OP_SET_TRAINING_CONTEXT_SPEED, "Set-training-context-speed (Training context operator)\r\n"
938 "\tTells FreeSpace that the player is expected to fly within a certain speed range. Once "
939 "this operator has been executed, you can measure how long they have been within this "
940 "speed range with the speed operator.\r\n\r\n"
941 "Takes 2 arguments:\r\n"
942 "\t1:\tMinimum speed of range player is to fly between.\r\n"
943 "\t2:\tMaximum speed of range player is to fly between." },
945 { OP_GRANT_PROMOTION, "Grant promotion (Action operator)\r\n"
946 "\tIn a single player game, this function grants a player an automatic promotion to the "
947 "next rank which the player can obtain. If he is already at the highest rank, this "
948 "operator has no effect. It takes no arguments." },
950 { OP_GRANT_MEDAL, "Grant medal (Action operator)\r\n"
951 "\tIn single player missions, this function grants the given medal to the player. "
952 "Currently, only 1 medal will be allowed to be given per mission.\r\n\r\n"
953 "Takes 1 argument:\r\n"
954 "\t1:\tName of medal to grant to player." },
956 { OP_GOOD_SECONDARY_TIME, "Set prefered secondary weapons\r\n"
957 "\tThis sexpression is used to inform the AI about prefered secondary weapons to "
958 "fire during combat. When this expression is evaulated, any AI ships of the given "
959 "team prefer to fire the given weapon at the given ship. (Prefered over other "
960 "secondary weapons)\r\n\r\n"
961 "Takes 4 argument:\r\n"
962 "\t1:\tTeam name which will prefer firing given weapon\r\n"
963 "\t2:\tMaximum number of this type of weapon above team can fire.\r\n"
964 "\t3:\tWeapon name (list includes only the valid weapons for this expression\r\n"
965 "\t4:\tShip name at which the above named team should fire the above named weapon." },
967 { OP_AND_IN_SEQUENCE, "And in sequence (Boolean operator)\r\n"
968 "\tReturns true if all of it's arguments have become true in the order they are "
970 "Returns a boolean value; Takes 2 or more boolean arguments." },
972 { OP_SKILL_LEVEL_AT_LEAST, "Skill level at least (Boolean operator)\r\n"
973 "\tReturns true if the player has selected the given skill level or higher.\r\n\r\n"
974 "Returns a boolean value; Takes 1 arguments:\r\n"
975 "\t1:\tName of the skill level to check." },
977 { OP_NUM_PLAYERS, "Num players (Status operator)\r\n"
978 "\tReturns the current number of players (multiplayer) playing in the current mission.\r\n\r\n"
979 "Returns a numeric value; Takes no arguments." },
981 { OP_IS_CARGO_KNOWN, "Is cargo known (Boolean operator)\r\n"
982 "\tReturns true if all of the specified objects' cargo is known by the player (i.e. they "
983 "have scanned each one.\r\n\r\n"
984 "Returns a boolean value; Takes 1 or more arguments:\r\n"
985 "\tAll:\tName of ship to check if it's cargo is known." },
987 { OP_HAS_BEEN_TAGGED_DELAY, "Has ship been tagged (delay) (Boolean operator)\r\n"
988 "\tReturns true if all of the specified ships have been tagged.\r\n\r\n"
989 "Returns a boolean value after <delay> seconds when all ships have been tagged; Takes 2 or more arguments:\r\n"
990 "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned."
991 "\tRest:\tNames of ships to check if tagged.." },
993 { OP_CAP_SUBSYS_CARGO_KNOWN_DELAY, "Is capital ship subsystem cargo known (delay) (Boolean operator)\r\n"
994 "\tReturns true if all of the specified subsystem cargo is known by the player.\r\n"
995 "\tNote: Cargo must be explicitly named.\r\n\r\n"
996 "Returns a boolean value after <delay> seconds when all cargo is known; Takes 3 or more arguments:\r\n"
997 "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned.\r\n"
998 "\t2:\tName of Captial ship\r\n"
999 "\tRest:\tNames of subsystems to check for cargo known.." },
1001 { OP_CARGO_KNOWN_DELAY, "Is cargo known (delay) (Boolean operator)\r\n"
1002 "\tReturns true if all of the specified objects' cargo is known by the player (i.e. they "
1003 "have scanned each one.\r\n\r\n"
1004 "Returns a boolean value after <delay> seconds when all cargo is known; Takes 2 or more arguments:\r\n"
1005 "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned."
1006 "\tRest:\tNames of ships/cargo to check for cargo known.." },
1008 { OP_WAS_PROMOTION_GRANTED, "Was promotion granted (Boolean operator)\r\n"
1009 "\tReturns true if a promotion was granted via the 'Grant promotion' operator in the mission.\r\n\r\n"
1010 "Returns a boolean value; Takes no arguments." },
1012 { OP_WAS_MEDAL_GRANTED, "Was medal granted (Boolean operator)\r\n"
1013 "\tReturns true if a medal was granted via via the 'Grant medal' operator in the mission. "
1014 "If you provide the optional argument to this operator, then true is only returned if the "
1015 "specified medal was granted.\r\n\r\n"
1016 "Returns a boolean value; Takes 0 or 1 arguments:\r\n"
1017 "\t1:\tName of medal to specifically check for (optional)." },
1019 { OP_GOOD_REARM_TIME, "Good rearm time (Action operator)\r\n"
1020 "\tInforms the game logic that right now is a good time for a given team to attempt to "
1021 "rearm their ships. The time parameter specified how long the \"good time\" will last.\r\n\r\n"
1022 "Takes 2 arguments:\r\n"
1023 "\t1:\tTeam Name\r\n"
1024 "\t2:\tTime in seconds rearm window should last" },
1026 { OP_ALLOW_SHIP, "Allow ship (Action operator)\r\n"
1027 "\tThis operator makes the given ship type available to the Terran team. Players will be "
1028 "able to have ships of this type in their starting wings in all future missions of this "
1030 "Takes 1 arguments:\r\n"
1031 "\t1:\tName of ship type (or ship class) to allow." },
1033 { OP_ALLOW_WEAPON, "Allow weapon (Action operator)\r\n"
1034 "\tThis operator makes the given weapon available to the Terran team. Players will be "
1035 "able to equip ships with in all future missions of this campaign.\r\n\r\n"
1036 "Takes 1 arguments:\r\n"
1037 "\t1:\tName of weapon (primary or secondary) to allow." },
1039 { OP_TECH_ADD_SHIP, "Tech add ship (Action operator)\r\n"
1040 "\tThis operator makes the given ship type available in the techroom database. Players will "
1041 "then be able to view this ship's specs there.\r\n\r\n"
1042 "Takes 1 or more arguments:\r\n"
1043 "\tAll:\tName of ship type (or ship class) to add." },
1045 { OP_TECH_ADD_WEAPON, "Tech add weapon (Action operator)\r\n"
1046 "\tThis operator makes the given weapon available in the techroom database. Players will "
1047 "then be able to view this weapon's specs there.\r\n\r\n"
1048 "Takes 1 or more arguments:\r\n"
1049 "\tAll:\tName of weapon (primary or secondary) to add." },
1051 { OP_AI_EVADE_SHIP, "Ai-evade ship (Ship goal)\r\n"
1052 "\tCauses the specified ship to go into evade mode and run away like the weak "
1053 "sally-boy it is.\r\n\r\n"
1054 "Takes 2 arguments:\r\n"
1055 "\t1:\tName of ship to evade from.\r\n"
1056 "\t2:\tGoal priority (number between 0 and 89)." },
1058 { OP_AI_STAY_NEAR_SHIP, "Ai-stay near ship (Ship goal)\r\n"
1059 "\tCauses the specified ship to keep itself near the given ship and not stray too far "
1060 "away from it.\r\n\r\n"
1061 "Takes 2 arguments:\r\n"
1062 "\t1:\tName of ship to stay near.\r\n"
1063 "\t2:\tGoal priority (number between 0 and 89)." },
1065 { OP_AI_KEEP_SAFE_DISTANCE, "Ai-keep safe distance (Ship goal)\r\n"
1066 "\tTells the specified ship to stay a safe distance away from any ship that isn't on the "
1067 "same team as it.\r\n\r\n"
1068 "Takes 1 argument:\r\n"
1069 "\t1:\tGoal priority (number between 0 and 89)." },
1071 { OP_AI_IGNORE, "Ai-ignore (Ship goal)\r\n"
1072 "\tTells the specified ship to ignore the given ship and not consider it as a valid "
1073 "target to attack.\r\n\r\n"
1074 "Takes 2 arguments:\r\n"
1075 "\t1:\tName of ship to ignore.\r\n"
1076 "\t2:\tGoal priority (number between 0 and 89)." },
1078 { OP_AI_STAY_STILL, "Ai-stay still (Ship goal)\r\n"
1079 "\tCauses the specified ship to stay still. The ship will do nothing until attacked at "
1080 "which time the ship will come to life and defend itself.\r\n\r\n"
1081 "Takes 2 arguments:\r\n"
1082 "\t1:\tShip or waypoint the ship staying still will directly face (currently not implemented)\r\n"
1083 "\t2:\tGoal priority (number between 0 and 89)." },
1085 { OP_AI_PLAY_DEAD, "Ai-play dead (Ship goal)\r\n"
1086 "\tCauses the specified ship to pretend that it is dead and not do anything. This "
1087 "expression should be used to indicate that a ship has no pilot and cannot respond "
1088 "to any enemy threats. A ship playing dead will not respond to any attack. This "
1089 "should really be named ai-is-dead\r\n\r\n"
1090 "Takes 1 argument:\r\n"
1091 "\t1:\tGoal priority (number between 0 and 89)." },
1093 { OP_FLASH_HUD_GAUGE, "Ai-flash hud gauge (Training goal)\r\n"
1094 "\tCauses the specified hud gauge to flash to draw the player's attention to it.\r\n\r\n"
1095 "Takes 1 argument:\r\n"
1096 "\t1:\tName of hud gauge to flash." },
1098 { OP_SHIP_VISIBLE, "ship-visible\r\n"
1099 "\tCauses the ships listed in this sexpression to be visible with player sensors.\r\n\r\n"
1100 "Takes 1 or more arguments:\r\n"
1101 "\t1+:\tName of ships to make visible to sensors." },
1103 { OP_SHIP_INVISIBLE, "ship-invisible\r\n"
1104 "\tCauses the ships listed in this sexpression to be invisible to player sensors.\r\n\r\n"
1105 "Takes 1 or more arguments:\r\n"
1106 "\t1+:\tName of ships to make invisible to sensors." },
1108 { OP_SHIP_VULNERABLE, "ship-vulnerable\r\n"
1109 "\tCauses the ship listed in this sexpression to be vulnerable to weapons.\r\n\r\n"
1110 "Takes 1 or more arguments:\r\n"
1111 "\t1+:\tName of ships to make vulnerable to weapons." },
1113 { OP_SHIP_INVULNERABLE, "ship-invulnerable\r\n"
1114 "\tCauses the ships listed in this sexpression to be invulnerable to weapons. Use with caution!!!!\r\n\r\n"
1115 "Takes 1 or more arguments:\r\n"
1116 "\t1+:\tName of ships to make invulnerable to weapons." },
1118 { OP_SHIP_GUARDIAN, "ship-guardian\r\n"
1119 "\tCauses the ships listed in this sexpression to not be killable by weapons. Use with caution!!!!\r\n\r\n"
1120 "Takes 1 or more arguments:\r\n"
1121 "\t1+:\tName of ships to make invulnerable to weapons." },
1123 { OP_SHIP_NO_GUARDIAN, "ship-no-guardian\r\n"
1124 "\tCauses the ships listed in this sexpression to be killable by weapons, if not invulnerable.\r\n\r\n"
1125 "Takes 1 or more arguments:\r\n"
1126 "\t1+:\tName of ships to make vulnerable to weapons." },
1128 { OP_PERCENT_SHIPS_DEPARTED, "percent-ships-departed\r\n"
1129 "\tBoolean function which returns true if the percentage of ships in the listed ships and wings "
1130 "which have departed is greater or equal to the given percentage. For wings, all ships of all waves "
1131 "are used for calculation for the total possible ships to depart.\r\n\r\n"
1132 "Takes 2 or more arguments:\r\n"
1133 "\t1:\tPercentge of departed ships at which this function will return true.\r\n"
1134 "\t2+:\tList of ships/wing whose departure status should be determined." },
1136 { OP_PERCENT_SHIPS_DESTROYED, "percent-ships-destroyed\r\n"
1137 "\tBoolean function which returns true if the percentage of ships in the listed ships and wings "
1138 "which have been destroyed is greater or equal to the given percentage. For wings, all ships of all waves "
1139 "are used for calculation for the total possible ships to be destroyed.\r\n\r\n"
1140 "Takes 2 or more arguments:\r\n"
1141 "\t1:\tPercentge of destroyed ships at which this function will return true.\r\n"
1142 "\t2+:\tList of ships/wing whose destroyed status should be determined." },
1144 { OP_RED_ALERT, "red-alert\r\n"
1145 "\tCauses Red Alert status in a mission. This function ends the current mission, and moves to "
1146 "the next mission in the campaign under red alert status. There should only be one branch from "
1147 "a mission that uses this expression\r\n\r\n"
1148 "Takes no arguments."},
1150 { OP_DEPART_NODE_DELAY, "depart-node-delay\r\n"
1151 "\tReturns true N seconds after the listed ships depart, if those ships depart within the "
1152 "radius of the given jump node. The delay value is given in seconds.\r\n\r\n"
1153 "Takes 3 or more arguments:r\n"
1154 "\t1:\tDelay in seconds after the last ship listed departe before this expression can return true.\r\n"
1155 "\t2:\tName of a jump node\r\n"
1156 "\t3+:\tList of ships to check for departure within radius of the jump node." },
1158 { OP_DESTROYED_DEPARTED_DELAY, "destroyed-or-departed-delay\r\n"
1159 "\tReturns true N seconds after all the listed ships or wings have been destroyed or have "
1161 "Takes 2 or more arguments:\r\n"
1162 "\t1:\tDelay in seconda after the last ship/wing is destroyed or departerd this expression can return true.\r\n"
1163 "\t2+:\tName of a ship or wing" },
1165 { OP_SPECIAL_CHECK, "Special-check\r\n"
1166 "\tDo some special check in training. Ask Mike K. about how it works.\r\n\r\n"
1167 "Returns a boolean value; Takes 1 argument:\r\n"
1168 "\t1:\tExtra special number (tm)" },
1170 { OP_END_CAMPAIGN, "end-campaign\r\n"
1171 "\tEnds the builtin campaign. Should only be used by the main FreeSpace campaign\r\n" },
1173 { OP_WARP_BROKEN, "break-warp\r\n"
1174 "\tBreak the warp drive on the specified ship. A broken warp drive can be repaired by "
1175 "a repair ship. Takes 1 or more arguments:\r\n"
1176 "\t1:\tList of ships to break the warp drive on" },
1177 { OP_WARP_NOT_BROKEN, "fix-warp\r\n"
1178 "\tFixes a broken warp drive instantaneously. This option applies to warp drives broken with "
1179 "the break-warp sepxression. Takes 1 or more arguments:\r\n"
1180 "\t1:\tList of ships whose warp drive should be fixed"},
1181 { OP_WARP_NEVER, "never-warp\r\n"
1182 "\tNever allows a ship to warp out. When this sexpression is used, the given ships will "
1183 "never be able to warp out. The warp drive cannot be repaired. Takes 1 or more arguments:\r\n"
1184 "\t1:\tList of ships whose are not allowed to warp out under any condition"},
1185 { OP_WARP_ALLOWED, "allow-warp\r\n"
1186 "\tAllows a ship which was previously not allowed to warp out to do so. When this sexpression is "
1187 "used, the given ships will be able to warp out again. Takes 1 or more arguments:\r\n"
1188 "\t1:\tList of ships whose are allowed to warp out"},
1189 { OP_JETTISON_CARGO, "jettison-cargo-delay\r\n"
1190 "\tCauses a cargo carrying ship to jettison its cargo without the undocking procedure. Takes 2 arguments"},
1191 { OP_BEAM_FIRE, "beam-fire\r\n"
1192 "\tFire a beam weapon from a specified subsystem\r\n"
1193 "\t1:\tShip which will be firing\r\n"
1194 "\t2:\tTurret which will fire the beam (note, this turret must have at least 1 beam weapon on it)\r\n"
1195 "\t3:\tShip which will be targeted\r\n"
1196 "Use add-data to add a specific subsystem to target on the specified target ship"},
1197 { OP_IS_TAGGED, "is-tagged\r\n"
1198 "\tReturns whether a given ship is tagged or not\r\n"},
1199 { OP_NUM_KILLS, "num-kills\r\n"
1200 "\tReturns the # of kills a player has. The ship specified in the first field should be the ship the player is in.\r\n"
1201 "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
1202 "\ttime there is no player in a given ship, this sexpression will return 0"},
1203 { OP_NUM_TYPE_KILLS, "num-type-kills\r\n"
1204 "\tReturns the # of kills a player has on a given ship type (fighter, bomber, cruiser, etc).\r\n"
1205 "The ship specified in the first field should be the ship the player is in.\r\n"
1206 "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
1207 "\ttime there is no player in a given ship, this sexpression will return 0"},
1208 { OP_NUM_CLASS_KILLS, "num-class-kills\r\n"
1209 "\tReturns the # of kills a player has on a specific ship class (Ulysses, Hercules, etc).\r\n"
1210 "The ship specified in the first field should be the ship the player is in.\r\n"
1211 "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
1212 "\ttime there is no player in a given ship, this sexpression will return 0"},
1213 { OP_BEAM_FREE, "beam-free\r\n"
1214 "\tSets one or more beam weapons to allow firing for a given ship\r\n"
1215 "\t1: Ship to be operated on\r\n"
1216 "\t2, 3, etc : List of turrets to activate\r\n"},
1217 { OP_BEAM_FREE_ALL, "beam-free-all\r\n"
1218 "\tSets all beam weapons on the specified ship to be active\r\n"},
1219 { OP_BEAM_LOCK, "beam-lock\r\n"
1220 "\tSets one or more beam weapons to NOT allow firing for a given ship\r\n"
1221 "\t1: Ship to be operated on\r\n"
1222 "\t2, 3, etc : List of turrets to deactivate\r\n"},
1223 { OP_BEAM_LOCK_ALL, "beam-lock-all\r\n"
1224 "\tSets all beam weapons on the specified ship to be deactivated\r\n"},
1225 { OP_TURRET_FREE, "turret-free\r\n"
1226 "\tSets one or more turret weapons to allow firing for a given ship\r\n"
1227 "\t1: Ship to be operated on\r\n"
1228 "\t2, 3, etc : List of turrets to activate\r\n"},
1229 { OP_TURRET_FREE_ALL, "turret-free-all\r\n"
1230 "\tSets all turret weapons on the specified ship to be active\r\n"},
1231 { OP_TURRET_LOCK, "turret-lock\r\n"
1232 "\tSets one or more turret weapons to NOT allow firing for a given ship\r\n"
1233 "\t1: Ship to be operated on\r\n"
1234 "\t2, 3, etc : List of turrets to deactivate\r\n"},
1235 { OP_TURRET_LOCK_ALL, "turret-lock-all\r\n"
1236 "\tSets all turret weapons on the specified ship to be deactivated\r\n"},
1237 { OP_ADD_REMOVE_ESCORT, "add-remove-escort\r\n"
1238 "\tAdds or removes a ship from an escort list.\r\n"
1239 "\t1: Ship to be added or removed\r\n"
1240 "\t2: 0 to remove from the list, any positive value to add to the list\r\n"
1241 "NOTE : it _IS_ safe to add a ship which may already be on the list or remove\r\n"
1242 "a ship which is not on the list\r\n"},
1243 { OP_AWACS_SET_RADIUS, "awacs-set-radius\r\n"
1244 "\tSets the awacs radius for a given ship subsystem. NOTE : does not work properly in multiplayer\r\n"
1245 "\t1: Ship which has the awacs subsystem\r\n"
1246 "\t2: Awacs subsystem\r\n"
1247 "\t3: New radius\r\n"},
1248 { OP_SEND_MESSAGE_LIST, "send-message-list\r\n"
1249 "\tSends a series of delayed messages. All times are accumulated"
1250 "\t1:\tName of who the message is from.\r\n"
1251 "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\").\r\n"
1252 "\t3:\tName of message (from message editor).\r\n"
1253 "\t4:\tDelay in ms\r\n"
1254 "Use Add-Data for multiple messages"
1255 "IMPORTANT : each additional message in the list MUST HAVE 4 entries\r\n"
1256 "any message without the 4 proper fields will be ignore, as will any\r\n"
1257 "successive messages"},
1258 { OP_CAP_WAYPOINT_SPEED, "cap-waypoint-speed\r\n"
1259 "\tSets the maximum speed of a ship while flying waypoints.\r\n"
1260 "\t1: Ship name\r\n"
1261 "\t2: Maximum speed while flying waypoints\r\n"
1262 "\tNOTE: This will only work if the ship is already in the game\r\n"
1263 "\tNOTE: Set to -1 to reset\r\n"},
1264 { OP_TURRET_TAGGED_ONLY_ALL, "turret-tagged-only\r\n"
1265 "\tMakes turrets target and hence fire strictly at tagged objects\r\n"
1266 "\t1: Ship name\r\n"
1267 "\tNOTE: Will not stop a turret already firing at an untagged ship\r\n"},
1268 { OP_TURRET_TAGGED_CLEAR_ALL, "turret-tagged-clear\r\n"
1269 "\tRelaxes restriction on turrets targeting only tagged ships\r\n"
1270 "\t1: Ship name\r\n"},
1271 { OP_SECONDARIES_DEPLETED, "secondaries-depleted\r\n"
1272 "\tReturns true if ship is out secondary weapons\r\n"
1273 "\t1: Ship name\r\n"},
1274 { OP_SUBSYS_SET_RANDOM, "subsys-set-random\r\n"
1275 "\tSets ship subsystem strength in a given range\r\n"
1276 "\t1: Ship name\r\n"
1277 "\t2: Low range\r\n"
1278 "\t3: High range\r\n"
1279 "\t4: List of subsys names not to be randomized\r\n"},
1280 { OP_SUPERNOVA_START, "supernova-start\r\n"
1281 "\t1: Time in seconds until the supernova shockwave hits the player\r\n"},
1282 { OP_SHIELD_RECHARGE_PCT, "shield-recharge-pct\r\n"
1283 "\tReturns a percentage from 0 to 100\r\n"
1284 "\t1: Ship name\r\n" },
1285 { OP_ENGINE_RECHARGE_PCT, "engine-recharge-pct\r\n"
1286 "\tReturns a percentage from 0 to 100\r\n"
1287 "\t1: Ship name\r\n" },
1288 { OP_WEAPON_RECHARGE_PCT, "weapon-recharge-pct\r\n"
1289 "\tReturns a percentage from 0 to 100\r\n"
1290 "\t1: Ship name\r\n" },
1291 { OP_CARGO_NO_DEPLETE, "cargo-no-deplete\r\n"
1292 "\tCauses the named ship to have unlimited cargo.\r\n"
1293 "\tNote: only applies to BIG or HUGE ships\r\n"
1294 "Takes 1 or more arguments:\r\n"
1295 "\t1:\tName of one of the ships.\r\n"
1296 "\t2:\toptional: 1 disallow depletion, 0 allow depletion." },
1297 { OP_SHIELD_QUAD_LOW, "shield-quad-low\r\n"
1298 "\tReturns true if the specified ship has a shield quadrant below\r\n"
1299 "\tthe specified threshold percentage\r\n"
1300 "\t1: Ship name\r\n"
1301 "\t2: Percentage\r\n" },
1302 { OP_SECONDARY_AMMO_PCT, "secondary-ammo-pct\r\n"
1303 "\tReturns the percentage of ammo remaining in the specified bank (0 to 100)\r\n"
1304 "\t1: Ship name\r\n"
1305 "\t2: Bank to check (0, 1, 2 are legal banks. 3 will return the cumulative average for all banks" },
1306 { OP_IS_SECONDARY_SELECTED, "is-secondary-selected\r\n"
1307 "\tReturns true if the specified bank is selected (0 .. num_banks - 1)\r\n"
1308 "\t1: Ship name\r\n"
1309 "\t2: Bank to check (0 .. num_banks - 1)\r\n"},
1310 { OP_IS_PRIMARY_SELECTED, "is-primary-selected\r\n"
1311 "\tReturns true if the specified bank is selected (0 .. num_banks - 1)\r\n"
1312 "\t1: Ship name\r\n"
1313 "\t2: Bank to check (0 .. num_banks - 1)\r\n"},
1314 { OP_SPECIAL_WARP_DISTANCE, "special-warp-dist\r\n"
1315 "\tReturns distance to the plane of the knossos device in percent length of ship\r\n"
1316 "\t(ie, 100 means front of ship is 1 ship length from plane of knossos device)\r\n"
1317 "\t1: Ship name\r\n"},
1318 { OP_SET_SPECIAL_WARPOUT_NAME, "special-warpout-name\r\n"
1319 "\tSets the name of the knossos device to be used for warpout\r\n"
1320 "\t1: Ship name to exit\r\n"
1321 "\t2: Name of knossos device\r\n"},
1322 { OP_SHIP_VANISH, "ship-vanish\r\n"
1323 "\tMakes the named ship vanish (no log and vanish)\r\n"
1324 "\tSingle Player Only! Warning: This will cause ship exit not to be logged, so 'has-departed', etc. will not work\r\n"
1325 "\t1: List of ship names to vanish\r\n"},
1326 { OP_IS_SHIP_VISIBLE, "is-ship-visible\r\n"
1327 "\tCheck whether ship is visible on Player's radar\r\n"
1328 "\tSingle Player Only! Returns 0 - not visible, 1 - partially visible, 2 - fully visible.\r\n"
1329 "\t1: Name of ship to check\r\n"},
1330 { OP_TEAM_SCORE, "team-score\r\n"
1331 "\tGet the score of a multi team vs team game.\r\n"
1332 "\t1: Team index (1 for team 1 and 2 for team 2)\r\n"},
1336 struct op_menu_struct {
1341 { "Objectives", OP_CATAGORY_OBJECTIVE },
1342 { "Time", OP_CATAGORY_TIME },
1343 { "Logical", OP_CATAGORY_LOGICAL },
1344 { "Arithmetic", OP_CATAGORY_ARITHMETIC },
1345 { "Status", OP_CATAGORY_STATUS },
1346 { "Change", OP_CATAGORY_CHANGE },
1347 { "Conditionals", OP_CATAGORY_CONDITIONAL },
1348 { "Debugging", OP_CATAGORY_DEBUG },
1349 { "Ai goals", OP_CATAGORY_AI },
1350 { "Event/Goals", OP_CATAGORY_GOAL_EVENT },
1351 { "Training", OP_CATAGORY_TRAINING },
1354 int Num_op_menus = sizeof(op_menu) / sizeof(op_menu_struct);
1357 sexp_tree::sexp_tree()
1359 select_sexp_node = -1;
1363 m_p_image_list = NULL;
1368 // clears out the tree, so all the nodes are unused.
1369 void sexp_tree::clear_tree(char *op)
1374 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1375 nodes[i].type = SEXPT_UNUSED;
1380 set_node(allocate_node(-1), (SEXPT_OPERATOR | SEXPT_VALID), op);
1386 void sexp_tree::reset_handles()
1390 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1391 nodes[i].handle = NULL;
1394 // initializes and creates a tree from a given sexp startpoint.
1395 void sexp_tree::load_tree(int index, char *deflt)
1402 cur = allocate_node(-1);
1403 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), deflt); // setup a default tree if none
1408 if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) { // handle numbers allender likes to use so much..
1409 cur = allocate_node(-1);
1410 if (atoi(Sexp_nodes[index].text))
1411 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "true");
1413 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "false");
1419 // assumption: first token is an operator. I require this because it would cause problems
1420 // with child/parent relations otherwise, and it should be this way anyway, since the
1421 // return type of the whole sexp is boolean, and only operators can satisfy this.
1422 Assert(Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR);
1423 load_branch(index, -1);
1427 void get_combined_variable_name(char *combined_name, const char *sexp_var_name)
1429 int sexp_var_index = get_index_sexp_variable_name(sexp_var_name);
1430 Assert(sexp_var_index > -1);
1432 sprintf(combined_name, "%s(%s)", Sexp_variables[sexp_var_index].variable_name, Sexp_variables[sexp_var_index].text);
1437 // creates a tree from a given Sexp_nodes[] point under a given parent. Recursive.
1438 void sexp_tree::load_branch(int index, int parent)
1441 char combined_var_name[2*TOKEN_LENGTH + 2];
1443 while (index != -1) {
1444 Assert(Sexp_nodes[index].type != SEXP_NOT_USED);
1445 if (Sexp_nodes[index].subtype == SEXP_ATOM_LIST) {
1446 load_branch(Sexp_nodes[index].first, parent); // do the sublist and continue
1448 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR) {
1449 cur = allocate_node(parent);
1450 if ((index == select_sexp_node) && !flag) { // translate sexp node to our node
1451 select_sexp_node = cur;
1455 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), Sexp_nodes[index].text);
1456 load_branch(Sexp_nodes[index].rest, cur); // operator is new parent now
1457 return; // 'rest' was just used, so nothing left to use.
1459 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) {
1460 cur = allocate_node(parent);
1461 if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
1462 get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
1463 set_node(cur, (SEXPT_VARIABLE | SEXPT_NUMBER | SEXPT_VALID), combined_var_name);
1465 set_node(cur, (SEXPT_NUMBER | SEXPT_VALID), Sexp_nodes[index].text);
1468 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_STRING) {
1469 cur = allocate_node(parent);
1470 if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
1471 get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
1472 set_node(cur, (SEXPT_VARIABLE | SEXPT_STRING | SEXPT_VALID), combined_var_name);
1474 set_node(cur, (SEXPT_STRING | SEXPT_VALID), Sexp_nodes[index].text);
1478 Assert(0); // unknown and/or invalid sexp type
1480 if ((index == select_sexp_node) && !flag) { // translate sexp node to our node
1481 select_sexp_node = cur;
1485 index = Sexp_nodes[index].rest;
1491 int sexp_tree::query_false(int node)
1497 Assert(nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID));
1498 Assert(nodes[node].next == -1); // must make this assumption or else it will confuse code!
1499 if (find_operator(nodes[node].text) == OP_FALSE){
1506 // builds an sexp of the tree and returns the index of it. This allocates sexp nodes.
1507 int sexp_tree::save_tree(int node)
1513 Assert(nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID));
1514 Assert(nodes[node].next == -1); // must make this assumption or else it will confuse code!
1515 return save_branch(node);
1518 // get variable name from sexp_tree node .text
1519 void var_name_from_sexp_tree_text(char *var_name, const char *text)
1521 int var_name_length = strcspn(text, "(");
1522 Assert(var_name_length < TOKEN_LENGTH - 1);
1524 strncpy(var_name, text, var_name_length);
1525 var_name[var_name_length] = '\0';
1528 #define NO_PREVIOUS_NODE -9
1529 // called recursively to save a tree branch and everything under it
1530 int sexp_tree::save_branch(int cur, int at_root)
1532 int start, node = -1, last = NO_PREVIOUS_NODE;
1533 char var_name_text[TOKEN_LENGTH];
1537 if (nodes[cur].type & SEXPT_OPERATOR) {
1538 node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, save_branch(nodes[cur].child));
1540 if ((nodes[cur].parent >= 0) && !at_root) {
1541 node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1);
1543 } else if (nodes[cur].type & SEXPT_NUMBER) {
1544 // allocate number, maybe variable
1545 if (nodes[cur].type & SEXPT_VARIABLE) {
1546 var_name_from_sexp_tree_text(var_name_text, nodes[cur].text);
1547 node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1);
1549 node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1);
1551 } else if (nodes[cur].type & SEXPT_STRING) {
1552 // allocate string, maybe variable
1553 if (nodes[cur].type & SEXPT_VARIABLE) {
1554 var_name_from_sexp_tree_text(var_name_text, nodes[cur].text);
1555 node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1);
1557 node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
1559 } else if (nodes[cur].type & SEXPT_STRING) {
1560 Assert( !(nodes[cur].type & SEXPT_VARIABLE) );
1563 Assert(0); // unknown and/or invalid type
1566 if (last == NO_PREVIOUS_NODE){
1568 } else if (last >= 0){
1569 Sexp_nodes[last].rest = node;
1573 Assert(last != NO_PREVIOUS_NODE); // should be impossible
1574 cur = nodes[cur].next;
1583 // allocate a node. Remains used until freed.
1584 int sexp_tree::allocate_node()
1588 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1589 if (nodes[i].type == SEXPT_UNUSED) {
1590 nodes[i].type = SEXPT_UNINIT;
1591 nodes[i].parent = -1;
1592 nodes[i].child = -1;
1598 Error(LOCATION, "Out of sexp tree nodes!");
1602 // allocate a child node under 'parent'. Appends to end of list.
1603 int sexp_tree::allocate_node(int parent, int after)
1605 int i, index = allocate_node();
1608 i = nodes[parent].child;
1610 nodes[parent].child = index;
1613 while ((i != after) && (nodes[i].next != -1))
1616 nodes[index].next = nodes[i].next;
1617 nodes[i].next = index;
1621 nodes[index].parent = parent;
1625 // free a node and all it's children. Also clears pointers to it, if any.
1626 // node = node chain to free
1627 // cascade = 0: free just this node and children under it. (default)
1628 // !0: free this node and all siblings after it.
1630 void sexp_tree::free_node(int node, int cascade)
1634 // clear the pointer to node
1635 i = nodes[node].parent;
1637 if (nodes[i].child == node)
1638 nodes[i].child = nodes[node].next;
1642 while (nodes[i].next != -1) {
1643 if (nodes[i].next == node) {
1644 nodes[i].next = nodes[node].next;
1653 nodes[node].next = -1;
1655 // now free up the node and it's children
1659 // more simple node freer, which works recursively. It frees the given node and all siblings
1660 // that come after it, as well as all children of these. Doesn't clear any links to any of
1661 // these freed nodes, so make sure all links are broken first. (i.e. use free_node() if you can)
1663 void sexp_tree::free_node2(int node)
1666 Assert(nodes[node].type != SEXPT_UNUSED);
1669 nodes[node].type = SEXPT_UNUSED;
1671 if (nodes[node].child != -1)
1672 free_node2(nodes[node].child);
1674 if (nodes[node].next != -1)
1675 free_node2(nodes[node].next);
1678 // initialize the data for a node. Should be called right after a new node is allocated.
1679 void sexp_tree::set_node(int node, int type, char *text)
1681 Assert(type != SEXPT_UNUSED);
1682 Assert(nodes[node].type != SEXPT_UNUSED);
1683 nodes[node].type = type;
1685 if (type & SEXPT_VARIABLE) {
1686 max_length = 2 * TOKEN_LENGTH + 2;
1688 max_length = TOKEN_LENGTH;
1690 Assert(strlen(text) < max_length);
1691 strcpy(nodes[node].text, text);
1694 void sexp_tree::post_load()
1697 select_sexp_node = -1;
1700 // build or rebuild a CTreeCtrl object with the current tree data
1701 void sexp_tree::build_tree()
1704 select_sexp_node = -1;
1707 add_sub_tree(0, TVI_ROOT);
1710 // Create the CTreeCtrl tree from the tree data. The tree data should already be setup by
1712 void sexp_tree::add_sub_tree(int node, HTREEITEM root)
1717 Assert(node >= 0 && node < MAX_SEXP_TREE_SIZE);
1718 node2 = nodes[node].child;
1720 // check for single argument operator case (prints as one line)
1721 /* if (node2 != -1 && nodes[node2].child == -1 && nodes[node2].next == -1) {
1722 sprintf(str, "%s %s", nodes[node].text, nodes[node2].text);
1723 nodes[node].handle = insert(str, root);
1724 nodes[node].flags = OPERAND | EDITABLE;
1725 nodes[node2].flags = COMBINED;
1729 // bitmap to draw in tree
1732 if (nodes[node].type & SEXPT_OPERATOR) {
1733 nodes[node].flags = OPERAND;
1734 bitmap = BITMAP_OPERATOR;
1736 if (nodes[node].type & SEXPT_VARIABLE) {
1737 nodes[node].flags = NOT_EDITABLE;
1738 bitmap = BITMAP_VARIABLE;
1740 nodes[node].flags = EDITABLE;
1741 bitmap = BITMAP_DATA;
1745 root = nodes[node].handle = insert(nodes[node].text, bitmap, bitmap, root);
1748 while (node != -1) {
1749 Assert(node >= 0 && node < MAX_SEXP_TREE_SIZE);
1750 Assert(nodes[node].type & SEXPT_VALID);
1751 if (nodes[node].type & SEXPT_OPERATOR) {
1752 add_sub_tree(node, root);
1755 Assert(nodes[node].child == -1);
1756 if (nodes[node].type & SEXPT_VARIABLE) {
1757 nodes[node].handle = insert(nodes[node].text, BITMAP_VARIABLE, BITMAP_VARIABLE, root);
1758 nodes[node].flags = NOT_EDITABLE;
1760 nodes[node].handle = insert(nodes[node].text, BITMAP_DATA, BITMAP_DATA, root);
1761 nodes[node].flags = EDITABLE;
1765 node = nodes[node].next;
1769 void sexp_tree::setup_selected(HTREEITEM h)
1776 item_handle = GetSelectedItem();
1778 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1779 if (nodes[i].handle == item_handle) {
1785 int sexp_tree::get_ambiguous_type(int parent)
1788 int first_arg_index = get_modify_variable_first_arg_index();
1789 int sexp_var_index = get_tree_name_to_sexp_variable_index(nodes[first_arg_index].text);
1790 Assert(sexp_var_index != -1);
1792 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
1795 return OPF_AMBIGUOUS;
1800 int sexp_tree::get_modify_variable_first_arg_index()
1804 Assert( item_index >= 0);
1806 // get parent and check "modify-variable"
1807 index = nodes[item_index].parent;
1808 Assert( index != -1 );
1809 Assert( !(stricmp(nodes[index].text, "modify-variable")) );
1811 // get first child and verify type variable
1812 index = nodes[index].child;
1813 Assert( index != -1 );
1814 Assert( nodes[index].type & SEXPT_VARIABLE);
1819 // handler for right mouse button clicks.
1820 void sexp_tree::right_clicked(int mode)
1823 int i, j, z, count, op, add_type, replace_type, type;
1824 sexp_list_item *list;
1827 POINT click_point, mouse;
1828 CMenu menu, *mptr, *popup_menu, *add_data_menu = NULL, *replace_data_menu = NULL;
1829 CMenu *add_op_menu, add_op_submenu[MAX_OP_MENUS];
1830 CMenu *replace_op_menu, replace_op_submenu[MAX_OP_MENUS];
1831 CMenu *insert_op_menu, insert_op_submenu[MAX_OP_MENUS];
1832 CMenu *replace_variable_menu = NULL;
1835 add_instance = replace_instance = -1;
1836 Assert(Num_operators <= MAX_OPERATORS);
1837 Assert(Num_op_menus < MAX_OP_MENUS);
1838 GetCursorPos(&mouse);
1839 click_point = mouse;
1840 ScreenToClient(&click_point);
1841 h = HitTest(CPoint(click_point), &_flags); // find out what they clicked on
1842 if (h && menu.LoadMenu(IDR_MENU_EDIT_SEXP_TREE)) {
1844 popup_menu = menu.GetSubMenu(0);
1845 ASSERT(popup_menu != NULL);
1846 SelectDropTarget(h);
1848 add_op_menu = replace_op_menu = insert_op_menu = NULL;
1850 // get pointers to several key popup menus we'll need to modify
1851 i = popup_menu->GetMenuItemCount();
1853 if ( (mptr = popup_menu->GetSubMenu(i)) > 0 ) {
1854 popup_menu->GetMenuString(i, buf, sizeof(buf), MF_BYPOSITION);
1856 if (!stricmp(buf, "add operator")) {
1859 } else if (!stricmp(buf, "replace operator")) {
1860 replace_op_menu = mptr;
1862 } else if (!stricmp(buf, "add data")) {
1863 add_data_menu = mptr;
1865 } else if (!stricmp(buf, "replace data")) {
1866 replace_data_menu = mptr;
1868 } else if (!stricmp(buf, "insert operator")) {
1869 insert_op_menu = mptr;
1871 } else if (!stricmp(buf, "replace variable")) {
1872 replace_variable_menu = mptr;
1877 // add popup menus for all the operator catagories
1878 for (i=0; i<Num_op_menus; i++) {
1879 add_op_submenu[i].CreatePopupMenu();
1880 replace_op_submenu[i].CreatePopupMenu();
1881 insert_op_submenu[i].CreatePopupMenu();
1882 add_op_menu->AppendMenu(MF_POPUP, (UINT) add_op_submenu[i].m_hMenu, op_menu[i].name);
1883 replace_op_menu->AppendMenu(MF_POPUP, (UINT) replace_op_submenu[i].m_hMenu, op_menu[i].name);
1884 insert_op_menu->AppendMenu(MF_POPUP, (UINT) insert_op_submenu[i].m_hMenu, op_menu[i].name);
1887 // get rid of the placeholders we needed to ensure popup menus stayed popup menus,
1888 // i.e. MSDEV will convert empty popup menus into normal menu items.
1889 add_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1890 replace_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1891 insert_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1892 replace_variable_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1897 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
1898 if (nodes[i].handle == h) {
1904 // Do SEXP_VARIABLE stuff here.
1905 if (m_mode != MODE_EVENTS) {
1906 // only allow variables in event mode
1907 menu.EnableMenuItem(ID_SEXP_TREE_ADD_VARIABLE, MF_GRAYED);
1908 menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED);
1910 menu.EnableMenuItem(ID_SEXP_TREE_ADD_VARIABLE, MF_ENABLED);
1911 menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_ENABLED);
1913 // check not root (-1)
1914 if (item_index >= 0) {
1915 // get type of sexp_tree item clicked on
1916 int type = get_type(h);
1918 int parent = nodes[item_index].parent;
1920 op = identify_operator(nodes[parent].text);
1922 int first_arg = nodes[parent].child;
1924 // get arg count of item to replace
1926 int temp = first_arg;
1927 while (temp != item_index) {
1929 temp = nodes[temp].next;
1931 // DB - added 3/4/99
1937 int op_type = query_operator_argument_type(op, Replace_count); // check argument type at this position
1939 // special case dont allow replace data for variable names
1940 if (op_type != OPF_AMBIGUOUS) {
1942 if ( (type & SEXPT_STRING) || (type & SEXPT_NUMBER) ) {
1944 int max_sexp_vars = MAX_SEXP_VARIABLES;
1945 // prevent collisions in id numbers: ID_VARIABLE_MENU + 512 = ID_ADD_MENU
1946 Assert(max_sexp_vars < 512);
1948 for (int idx=0; idx<max_sexp_vars; idx++) {
1949 if (Sexp_variables[idx].type & SEXP_VARIABLE_SET) {
1950 UINT flag = MF_STRING | MF_GRAYED;
1951 // maybe gray flag MF_GRAYED
1953 // get type -- gray "string" or number accordingly
1954 if ( type & SEXPT_STRING ) {
1955 if ( Sexp_variables[idx].type & SEXP_VARIABLE_STRING ) {
1958 } else if ( type & SEXPT_NUMBER ) {
1959 if ( Sexp_variables[idx].type & SEXP_VARIABLE_NUMBER ) {
1964 // if modify-variable and changing variable, enable all variables
1965 if (op_type == OPF_VARIABLE_NAME) {
1966 Modify_variable = 1;
1969 Modify_variable = 0;
1973 // append list of variable names and values
1974 // set id as ID_VARIABLE_MENU + idx
1975 sprintf(buf, "%s(%s)", Sexp_variables[idx].variable_name, Sexp_variables[idx].text);
1977 replace_variable_menu->AppendMenu(flag, (ID_VARIABLE_MENU + idx), buf);
1985 // cant modify if no variables
1986 if (sexp_variable_count() == 0) {
1987 menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED);
1991 // add operator menu items to the various catagory submenus they belong in
1992 for (i=0; i<Num_operators; i++) {
1993 for (j=0; j<Num_op_menus; j++) {
1994 if (op_menu[j].id == (Operators[i].value & OP_CATAGORY_MASK)) {
1997 switch (Operators[i].value) {
1998 case OP_WAS_PROMOTION_GRANTED:
1999 case OP_WAS_MEDAL_GRANTED:
2000 case OP_GRANT_PROMOTION:
2001 case OP_GRANT_MEDAL:
2002 case OP_TECH_ADD_SHIP:
2003 case OP_TECH_ADD_WEAPON:
2004 j = Num_op_menus; // don't allow these operators in final release
2007 if (j < Num_op_menus) {
2008 add_op_submenu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value, Operators[i].text);
2009 replace_op_submenu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value | OP_REPLACE_FLAG, Operators[i].text);
2010 insert_op_submenu[j].AppendMenu(MF_STRING, Operators[i].value | OP_INSERT_FLAG, Operators[i].text);
2013 break; // only 1 category valid
2018 // find local index (i) of current item (from it's handle)
2019 SelectItem(item_handle = h);
2020 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
2021 if (nodes[i].handle == h) {
2026 // special case: item is a ROOT node, and a label that can be edited (not an item in the sexp tree)
2027 if ((item_index == -1) && (m_mode & ST_LABELED_ROOT)) {
2028 if (m_mode & ST_ROOT_EDITABLE) {
2029 menu.EnableMenuItem(ID_EDIT_TEXT, MF_ENABLED);
2031 menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED);
2034 // disable copy, insert op
2035 menu.EnableMenuItem(ID_EDIT_COPY, MF_GRAYED);
2036 for (j=0; j<Num_operators; j++) {
2037 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2040 gray_menu_tree(popup_menu);
2041 popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this);
2045 Assert(item_index != -1); // handle not found, which should be impossible.
2046 if (!(nodes[item_index].flags & EDITABLE)) {
2047 menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED);
2050 if (nodes[item_index].parent == -1) { // root node
2051 menu.EnableMenuItem(ID_DELETE, MF_GRAYED); // can't delete the root item.
2054 /* if ((nodes[item_index].flags & OPERAND) && (nodes[item_index].flags & EDITABLE)) // expandable?
2055 menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
2057 z = nodes[item_index].child;
2058 if (z != -1 && nodes[z].next == -1 && nodes[z].child == -1)
2059 menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
2061 z = nodes[nodes[item_index].parent].child;
2062 if (z != -1 && nodes[z].next == -1 && nodes[z].child == -1)
2063 menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);*/
2065 // change enabled status of 'add' type menu options.
2067 if (nodes[item_index].flags & OPERAND) {
2068 add_type = OPR_STRING;
2069 int child = nodes[item_index].child;
2070 Add_count = count_args(child);
2071 op = identify_operator(nodes[item_index].text);
2074 // get listing of valid argument values and add to menus
2075 type = query_operator_argument_type(op, Add_count);
2076 list = get_listing_opf(type, item_index, Add_count);
2078 sexp_list_item *ptr;
2084 // enable operators with correct return type
2085 menu.EnableMenuItem(Operators[ptr->op].value, MF_ENABLED);
2089 if ( (data_idx + 3) % 30) {
2090 add_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text);
2092 add_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text);
2101 // special handling for the non-string formats
2102 if (type == OPF_NONE) { // an argument can't be added
2105 } else if (type == OPF_NULL) { // arguments with no return values
2106 add_type = OPR_NULL;
2108 } else if (type == OPF_NUMBER) { // takes numbers
2109 add_type = OPR_NUMBER;
2110 menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED);
2112 } else if (type == OPF_POSITIVE) { // takes non-negative numbers
2113 add_type = OPR_POSITIVE;
2114 menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED);
2116 } else if (type == OPF_BOOL) { // takes true/false bool values
2117 add_type = OPR_BOOL;
2119 } else if (type == OPF_AI_GOAL) {
2120 add_type = OPR_AI_GOAL;
2123 // add_type unchanged from above
2124 if (add_type == OPR_STRING) {
2125 menu.EnableMenuItem(ID_ADD_STRING, MF_ENABLED);
2131 // disable operators that do not have arguments available
2132 for (j=0; j<Num_operators; j++) {
2133 if (!query_default_argument_available(j)) {
2134 menu.EnableMenuItem(Operators[j].value, MF_GRAYED);
2139 // change enabled status of 'replace' type menu options.
2141 int parent = nodes[item_index].parent;
2143 replace_type = OPR_STRING;
2144 op = identify_operator(nodes[parent].text);
2146 int first_arg = nodes[parent].child;
2147 count = count_args(nodes[parent].child);
2149 // already at minimum number of arguments?
2150 if (count <= Operators[op].min) {
2151 menu.EnableMenuItem(ID_DELETE, MF_GRAYED);
2154 // get arg count of item to replace
2156 int temp = first_arg;
2157 while (temp != item_index) {
2159 temp = nodes[temp].next;
2161 // DB - added 3/4/99
2167 // maybe gray delete
2168 for (i=Replace_count+1; i<count; i++) {
2169 if (query_operator_argument_type(op, i-1) != query_operator_argument_type(op, i)) {
2170 menu.EnableMenuItem(ID_DELETE, MF_GRAYED);
2175 type = query_operator_argument_type(op, Replace_count); // check argument type at this position
2177 // special case reset type for ambiguous
2178 if (type == OPF_AMBIGUOUS) {
2179 type = get_ambiguous_type(parent);
2182 list = get_listing_opf(type, parent, Replace_count);
2184 // special case dont allow replace data for variable names
2185 if ( (type != OPF_VARIABLE_NAME) && list) {
2186 sexp_list_item *ptr;
2192 menu.EnableMenuItem(Operators[ptr->op].value | OP_REPLACE_FLAG, MF_ENABLED);
2195 if ( (data_idx + 3) % 30)
2196 replace_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text);
2198 replace_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text);
2206 if (type == OPF_NONE) { // takes no arguments
2209 } else if (type == OPF_NUMBER) { // takes numbers
2210 replace_type = OPR_NUMBER;
2211 menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
2213 } else if (type == OPF_POSITIVE) { // takes non-negative numbers
2214 replace_type = OPR_POSITIVE;
2215 menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
2217 } else if (type == OPF_BOOL) { // takes true/false bool values
2218 replace_type = OPR_BOOL;
2220 } else if (type == OPF_NULL) { // takes operator that doesn't return a value
2221 replace_type = OPR_NULL;
2223 } else if (type == OPF_AI_GOAL) {
2224 replace_type = OPR_AI_GOAL;
2227 // default to string
2228 if (replace_type == OPR_STRING) {
2229 menu.EnableMenuItem(ID_REPLACE_STRING, MF_ENABLED);
2232 // modify string or number if (modify_variable)
2233 if ( !stricmp(Operators[op].text, "modify-variable") ) {
2234 int modify_type = get_modify_variable_type();
2236 if (modify_type == OPF_NUMBER) {
2237 menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
2238 menu.EnableMenuItem(ID_REPLACE_STRING, MF_GRAYED);
2240 // no change for string type
2245 } else { // top node, so should be a Boolean type.
2246 if (m_mode == MODE_EVENTS) { // return type should be null
2247 replace_type = OPR_NULL;
2248 for (j=0; j<Num_operators; j++)
2249 if (query_operator_return_type(j) == OPR_NULL)
2250 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_ENABLED);
2253 replace_type = OPR_BOOL;
2254 for (j=0; j<Num_operators; j++)
2255 if (query_operator_return_type(j) == OPR_BOOL)
2256 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_ENABLED);
2260 for (j=0; j<Num_operators; j++)
2261 if (!query_default_argument_available(j))
2262 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_GRAYED);
2264 // change enabled status of 'insert' type menu options.
2265 z = nodes[item_index].parent;
2268 op = identify_operator(nodes[z].text);
2272 while (j != item_index) {
2277 type = query_operator_argument_type(op, count); // check argument type at this position
2280 if (m_mode == MODE_EVENTS)
2286 for (j=0; j<Num_operators; j++) {
2287 z = query_operator_return_type(j);
2288 if (!sexp_query_type_match(type, z) || (Operators[j].min < 1))
2289 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2291 z = query_operator_argument_type(j, 0);
2292 if ((type == OPF_NUMBER) && (z == OPF_POSITIVE))
2296 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2299 for (j=0; j<Num_operators; j++)
2300 if (!query_default_argument_available(j))
2301 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2303 // disable non campaign operators if in campaign mode
2304 for (j=0; j<Num_operators; j++) {
2306 if (m_mode == MODE_CAMPAIGN) {
2307 if (Operators[j].value & OP_NONCAMPAIGN_FLAG)
2311 if (Operators[j].value & OP_CAMPAIGN_ONLY_FLAG)
2316 menu.EnableMenuItem(Operators[j].value, MF_GRAYED);
2317 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_GRAYED);
2318 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2322 if ((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)) {
2323 Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
2325 if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
2326 j = find_operator(CTEXT(Sexp_clipboard));
2328 z = query_operator_return_type(j);
2330 if ((z == OPR_POSITIVE) && (replace_type == OPR_NUMBER))
2333 if (replace_type == z)
2334 menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2336 z = query_operator_return_type(j);
2337 if ((z == OPR_POSITIVE) && (add_type == OPR_NUMBER))
2341 menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2343 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
2344 if ((replace_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1))
2345 menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2347 else if (replace_type == OPR_NUMBER)
2348 menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2350 if ((add_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1))
2351 menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2353 else if (add_type == OPR_NUMBER)
2354 menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2356 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
2357 if (replace_type == OPR_STRING)
2358 menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2360 if (add_type == OPR_STRING)
2361 menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2364 Int3(); // unknown and/or invalid sexp type
2367 if (!(menu.GetMenuState(ID_DELETE, MF_BYCOMMAND) & MF_GRAYED))
2368 menu.EnableMenuItem(ID_EDIT_CUT, MF_ENABLED);
2370 gray_menu_tree(popup_menu);
2371 popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this);
2375 // counts the number of arguments an operator has. Call this with the node of the first
2376 // argument of the operator
2377 int sexp_tree::count_args(int node)
2381 while (node != -1) {
2383 node = nodes[node].next;
2389 // identify what type of argument this is. You call it with the node of the first argument
2390 // of an operator. It will search through enough of the arguments to determine what type of
2392 int sexp_tree::identify_arg_type(int node)
2396 while (node != -1) {
2397 Assert(nodes[node].type & SEXPT_VALID);
2398 switch (SEXPT_TYPE(nodes[node].type)) {
2399 case SEXPT_OPERATOR:
2400 type = find_operator(nodes[node].text);
2402 return query_operator_return_type(type);
2407 case SEXPT_STRING: // either a ship or a wing
2408 type = SEXP_ATOM_STRING;
2409 break; // don't return, because maybe we can narrow selection down more.
2412 node = nodes[node].next;
2418 // determine if an item should be editable. This doesn't actually edit the label.
2419 int sexp_tree::edit_label(HTREEITEM h)
2423 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
2424 if (nodes[i].handle == h) {
2429 // Check if tree root
2430 if (i == MAX_SEXP_TREE_SIZE) {
2431 if (m_mode & ST_ROOT_EDITABLE) {
2438 // Operators are editable
2439 if (nodes[i].type & SEXPT_OPERATOR) {
2443 // Variables must be edited through dialog box
2444 if (nodes[i].type & SEXPT_VARIABLE) {
2448 // Don't edit if not flaged as editable
2449 if (!(nodes[i].flags & EDITABLE)) {
2453 // Otherwise, allow editing
2457 if (nodes[i].flags & OPERAND) {
2458 data = nodes[i].child;
2460 SetItemText(h, nodes[i].text);
2461 nodes[i].flags = OPERAND;
2462 item_handle = nodes[data].handle = insert(nodes[data].text, nodes[data].type, nodes[data].flags, h);
2463 nodes[data].flags = EDITABLE;
2464 Expand(h, TVE_EXPAND);
2465 SelectItem(item_handle);
2471 int sexp_tree::end_label_edit(HTREEITEM h, char *str)
2473 int len, node, r = 1;
2479 for (node=0; node<MAX_SEXP_TREE_SIZE; node++)
2480 if (nodes[node].handle == h)
2483 if (node == MAX_SEXP_TREE_SIZE) {
2484 if (m_mode == MODE_EVENTS) {
2485 item_index = GetItemData(h);
2486 Assert(Event_editor_dlg);
2487 node = Event_editor_dlg->handler(ROOT_RENAMED, item_index, str);
2491 Int3(); // root labels shouldn't have been editable!
2494 Assert(node < MAX_SEXP_TREE_SIZE);
2495 if (nodes[node].type & SEXPT_OPERATOR) {
2496 str = match_closest_operator(str, node);
2497 SetItemText(h, str);
2499 add_or_replace_operator(identify_operator(str), 1);
2503 // Error checking would not hurt here
2505 if (len >= TOKEN_LENGTH)
2506 len = TOKEN_LENGTH - 1;
2508 strncpy(nodes[node].text, str, len);
2509 nodes[node].text[len] = 0;
2511 /* node = nodes[node].parent;
2513 child = nodes[node].child;
2514 if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) {
2515 merge_operator(child);
2523 // Check if 'op' is a valid operator match for operator format (OPF) 'type'. Returns true if it
2524 // is, false if not.
2526 int sexp_tree::check_operator_validity(int op, int type)
2530 rtype = query_operator_return_type(op);
2533 return ( (rtype == OPR_NUMBER) || (rtype == OPR_POSITIVE) );
2536 return (rtype == OPR_POSITIVE);
2539 return (rtype == OPR_BOOL);
2542 return (rtype == OPR_NULL);
2545 return (rtype == OPR_AI_GOAL);
2551 // Look for the valid operator that is the closest match for 'str' and return the operator
2552 // number of it. What operators are valid is determined by 'node', and an operator is valid
2553 // if it is allowed to fit at position 'node'
2555 char *sexp_tree::match_closest_operator(char *str, int node)
2557 int z, n, i, op, arg_num, type;
2558 char *sub_best = NULL, *best = NULL;
2560 z = nodes[node].parent;
2565 op = identify_operator(nodes[z].text);
2569 // determine which argument we are of the parent
2578 type = query_operator_argument_type(op, arg_num); // check argument type at this position
2579 for (i=0; i<Num_operators; i++) {
2580 if (check_operator_validity(i, type)) {
2581 if ( (stricmp(str, Operators[i].text) <= 0) && (!best || (stricmp(str, best) < 0)) )
2582 best = Operators[i].text;
2584 if ( !sub_best || (stricmp(Operators[i].text, sub_best) > 0) )
2585 sub_best = Operators[i].text;
2590 best = sub_best; // no best found, use our plan #2 best found.
2592 Assert(best); // we better have some valid operator at this point.
2598 if (nodes[node].flags == EDITABLE) // data
2599 node = nodes[node].parent;
2602 child = nodes[node].child;
2603 if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) {
2604 sprintf(buf, "%s %s", nodes[node].text, nodes[child].text);
2605 SetItemText(nodes[node].handle, buf);
2606 nodes[node].flags = OPERAND | EDITABLE;
2607 nodes[child].flags = COMBINED;
2608 DeleteItem(nodes[child].handle);
2609 nodes[child].handle = NULL;
2615 // this really only handles messages generated by the right click popup menu
2616 BOOL sexp_tree::OnCommand(WPARAM wParam, LPARAM lParam)
2618 int i, z, id, node, op, type;
2619 sexp_list_item *list, *ptr;
2622 if ((item_index >= 0) && (item_index < total))
2623 item_handle = nodes[item_index].handle;
2625 id = LOWORD(wParam);
2629 if (id == ID_SEXP_TREE_ADD_VARIABLE) {
2630 CAddVariableDlg dlg;
2633 if ( dlg.m_create ) {
2637 if ( dlg.m_type_number ) {
2638 type = SEXP_VARIABLE_NUMBER;
2640 type = SEXP_VARIABLE_STRING;
2644 sexp_add_variable(dlg.m_default_value, dlg.m_variable_name, type);
2647 sexp_variable_sort();
2653 if (id == ID_SEXP_TREE_MODIFY_VARIABLE) {
2654 CModifyVariableDlg dlg;
2656 // get sexp_variable index for item index
2657 dlg.m_start_index = get_item_index_to_var_index();
2659 // get pointer to tree
2660 dlg.m_p_sexp_tree = this;
2664 Assert( !(dlg.m_deleted && dlg.m_do_modify) );
2666 if (dlg.m_deleted) {
2667 // find index in sexp_variable list
2668 int sexp_var_index = get_index_sexp_variable_name(dlg.m_cur_variable_name);
2669 Assert(sexp_var_index != -1);
2672 sexp_variable_delete(sexp_var_index);
2675 sexp_variable_sort();
2677 // delete from sexp_tree, replacing with "number" or "string" as needed
2678 // further error checking from add_data()
2679 delete_sexp_tree_variable(dlg.m_cur_variable_name);
2684 if (dlg.m_do_modify) {
2685 // check sexp_tree -- warn on type
2686 // find index and change either (1) name, (2) type, (3) value
2687 int sexp_var_index = get_index_sexp_variable_name(dlg.m_old_var_name);
2688 Assert(sexp_var_index != -1);
2690 // save old name, since name may be modified
2691 char old_name[TOKEN_LENGTH];
2692 strcpy(old_name, Sexp_variables[sexp_var_index].variable_name);
2696 if (dlg.m_type_number) {
2697 type = SEXP_VARIABLE_NUMBER;
2699 type = SEXP_VARIABLE_STRING;
2702 // update sexp_variable
2703 sexp_fred_modify_variable(dlg.m_default_value, dlg.m_cur_variable_name, sexp_var_index, type);
2706 modify_sexp_tree_variable(old_name, sexp_var_index);
2708 // Don't sort until after modify, since modify uses index
2709 if (dlg.m_modified_name) {
2710 sexp_variable_sort();
2721 // check if REPLACE_VARIABLE_MENU
2722 if ( (id >= ID_VARIABLE_MENU) && (id < ID_VARIABLE_MENU + 511)) {
2724 Assert(item_index >= 0);
2726 // get index into list of type valid variables
2727 int var_idx = id - ID_VARIABLE_MENU;
2728 Assert( (var_idx >= 0) && (var_idx < MAX_SEXP_VARIABLES) );
2730 int type = get_type(item_handle);
2731 Assert( (type & SEXPT_NUMBER) || (type & SEXPT_STRING) );
2733 // dont do type check for modify-variable
2734 if (Modify_variable) {
2735 if (Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER) {
2736 type = SEXPT_NUMBER;
2737 } else if (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) {
2738 type = SEXPT_STRING;
2740 Int3(); // unknown type
2745 // verify type in tree is same as type in Sexp_variables array
2746 if (type & SEXPT_NUMBER) {
2747 Assert(Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER);
2750 if (type & SEXPT_STRING) {
2751 Assert( (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) );
2756 replace_variable_data(var_idx, (type | SEXPT_VARIABLE));
2762 if ((id >= ID_ADD_MENU) && (id < ID_ADD_MENU + 511)) {
2763 Assert(item_index >= 0);
2764 op = identify_operator(nodes[item_index].text);
2767 type = query_operator_argument_type(op, Add_count);
2768 list = get_listing_opf(type, item_index, Add_count);
2779 Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0));
2780 expand_operator(item_index);
2781 add_data(ptr->text, ptr->type);
2786 if ((id >= ID_REPLACE_MENU) && (id < ID_REPLACE_MENU + 511)) {
2787 Assert(item_index >= 0);
2788 Assert(nodes[item_index].parent >= 0);
2789 op = identify_operator(nodes[nodes[item_index].parent].text);
2792 type = query_operator_argument_type(op, Replace_count); // check argument type at this position
2793 list = get_listing_opf(type, nodes[item_index].parent, Replace_count);
2796 id -= ID_REPLACE_MENU;
2804 Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0));
2805 expand_operator(item_index);
2806 replace_data(ptr->text, ptr->type);
2811 for (op=0; op<Num_operators; op++) {
2812 if (id == Operators[op].value) {
2813 add_or_replace_operator(op);
2817 if (id == (Operators[op].value | OP_REPLACE_FLAG)) {
2818 add_or_replace_operator(op, 1);
2822 if (id == (Operators[op].value | OP_INSERT_FLAG)) {
2825 z = nodes[item_index].parent;
2826 flags = nodes[item_index].flags;
2827 node = allocate_node(z, item_index);
2828 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text);
2829 nodes[node].flags = flags;
2831 h = nodes[z].handle;
2834 h = GetParentItem(nodes[item_index].handle);
2835 if (m_mode == MODE_GOALS) {
2836 Assert(Goal_editor_dlg);
2837 Goal_editor_dlg->insert_handler(item_index, node);
2838 SetItemData(h, node);
2840 } else if (m_mode == MODE_EVENTS) {
2841 Assert(Event_editor_dlg);
2842 Event_editor_dlg->insert_handler(item_index, node);
2843 SetItemData(h, node);
2845 } else if (m_mode == MODE_CAMPAIGN) {
2846 Campaign_tree_formp->insert_handler(item_index, node);
2847 SetItemData(h, node);
2855 item_handle = nodes[node].handle = insert(Operators[op].text, BITMAP_OPERATOR, BITMAP_OPERATOR, h, nodes[item_index].handle);
2856 move_branch(item_index, node);
2859 for (i=1; i<Operators[op].min; i++)
2860 add_default_operator(op, i);
2862 Expand(item_handle, TVE_EXPAND);
2870 // If a clipboard already exist, unmark it as persistent and free old clipboard
2871 if (Sexp_clipboard != -1) {
2872 sexp_unmark_persistent(Sexp_clipboard);
2873 free_sexp2(Sexp_clipboard);
2876 // Allocate new clipboard and mark persistent
2877 Sexp_clipboard = save_branch(item_index, 1);
2878 sexp_mark_persistent(Sexp_clipboard);
2882 // the following assumptions are made..
2883 Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
2884 Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
2886 if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
2887 expand_operator(item_index);
2888 replace_operator(CTEXT(Sexp_clipboard));
2889 if (Sexp_nodes[Sexp_clipboard].rest != -1) {
2890 load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index);
2891 i = nodes[item_index].child;
2893 add_sub_tree(i, nodes[item_index].handle);
2898 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
2899 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2900 expand_operator(item_index);
2901 replace_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID));
2903 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
2904 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2905 expand_operator(item_index);
2906 replace_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
2909 Assert(0); // unknown and/or invalid sexp type
2913 case ID_EDIT_PASTE_SPECIAL: // add paste, instead of replace.
2914 // the following assumptions are made..
2915 Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
2916 Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
2918 if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
2919 expand_operator(item_index);
2920 add_operator(CTEXT(Sexp_clipboard));
2921 if (Sexp_nodes[Sexp_clipboard].rest != -1) {
2922 load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index);
2923 i = nodes[item_index].child;
2925 add_sub_tree(i, nodes[item_index].handle);
2930 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
2931 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2932 expand_operator(item_index);
2933 add_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID));
2935 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
2936 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2937 expand_operator(item_index);
2938 add_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
2941 Assert(0); // unknown and/or invalid sexp type
2945 /* case ID_SPLIT_LINE:
2946 if ((nodes[item_index].flags & OPERAND) && (nodes[item_index].flags & EDITABLE)) // expandable?
2947 expand_operator(item_index);
2949 merge_operator(item_index);
2954 expand_branch(item_handle);
2958 if (edit_label(item_handle)) {
2960 EditLabel(item_handle);
2965 case ID_ADD_STRING: {
2968 node = add_data("string", (SEXPT_STRING | SEXPT_VALID));
2969 EditLabel(nodes[node].handle);
2973 case ID_ADD_NUMBER: {
2976 node = add_data("number", (SEXPT_NUMBER | SEXPT_VALID));
2977 EditLabel(nodes[node].handle);
2982 if (Sexp_clipboard != -1) {
2983 sexp_unmark_persistent(Sexp_clipboard);
2984 free_sexp2(Sexp_clipboard);
2987 Sexp_clipboard = save_branch(item_index, 1);
2988 sexp_mark_persistent(Sexp_clipboard);
2989 // fall through to ID_DELETE case.
2995 if ((m_mode & ST_ROOT_DELETABLE) && (item_index == -1)) {
2996 item_index = GetItemData(item_handle);
2997 if (m_mode == MODE_GOALS) {
2998 Assert(Goal_editor_dlg);
2999 node = Goal_editor_dlg->handler(ROOT_DELETED, item_index);
3001 } else if (m_mode == MODE_EVENTS) {
3002 Assert(Event_editor_dlg);
3003 node = Event_editor_dlg->handler(ROOT_DELETED, item_index);
3006 Assert(m_mode == MODE_CAMPAIGN);
3007 node = Campaign_tree_formp->handler(ROOT_DELETED, item_index);
3012 DeleteItem(item_handle);
3017 Assert(item_index >= 0);
3018 h = GetParentItem(item_handle);
3019 parent = nodes[item_index].parent;
3020 if ((parent == -1) && (m_mode == MODE_EVENTS))
3021 Int3(); // no longer used, temporary to check if called still.
3023 Assert(parent != -1 && nodes[parent].handle == h);
3024 free_node(item_index);
3025 DeleteItem(item_handle);
3027 node = nodes[parent].child;
3028 /* if (node != -1 && nodes[node].next == -1 && nodes[node].child == -1) {
3029 sprintf(buf, "%s %s", nodes[parent].text, nodes[node].text);
3030 SetItem(h, TVIF_TEXT, buf, 0, 0, 0, 0, 0);
3031 nodes[parent].flags = OPERAND | EDITABLE;
3032 nodes[node].flags = COMBINED;
3033 DeleteItem(nodes[node].handle);
3041 return CTreeCtrl::OnCommand(wParam, lParam);
3044 // adds to or replaces (based on passed in flag) the current operator
3045 void sexp_tree::add_or_replace_operator(int op, int replace_flag)
3047 int i, op_index, op2;
3049 op_index = item_index;
3051 if (nodes[item_index].flags & OPERAND) { // are both operators?
3052 op2 = identify_operator(nodes[item_index].text);
3054 i = count_args(nodes[item_index].child);
3055 if ((i >= Operators[op].min) && (i <= Operators[op].max)) { // are old num args valid?
3057 if (query_operator_argument_type(op2, i) != query_operator_argument_type(op, i)) // does each arg match expected type?
3060 if (i < 0) { // everything is ok, so we can keep old arguments with new operator
3061 set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text);
3062 SetItemText(nodes[item_index].handle, Operators[op].text);
3063 nodes[item_index].flags = OPERAND;
3069 replace_operator(Operators[op].text);
3072 add_operator(Operators[op].text);
3074 // fill in all the required (minimum) arguments with default values
3075 for (i=0; i<Operators[op].min; i++)
3076 add_default_operator(op, i);
3078 Expand(item_handle, TVE_EXPAND);
3081 // initialize node, type operator
3083 void sexp_list_item::set_op(int op_num)
3087 if (op_num >= FIRST_OP) { // do we have an op value instead of an op number (index)?
3088 for (i=0; i<Num_operators; i++)
3089 if (op_num == Operators[i].value)
3090 op_num = i; // convert op value to op number
3094 text = Operators[op].text;
3095 type = (SEXPT_OPERATOR | SEXPT_VALID);
3098 // initialize node, type data
3099 // Defaults: t = SEXPT_STRING
3101 void sexp_list_item::set_data(char *str, int t)
3108 // add a node to end of list
3110 void sexp_list_item::add_op(int op_num)
3112 sexp_list_item *item, *ptr;
3114 item = new sexp_list_item;
3120 item->set_op(op_num);
3123 // add a node to end of list
3124 // Defaults: t = SEXPT_STRING
3126 void sexp_list_item::add_data(char *str, int t)
3128 sexp_list_item *item, *ptr;
3130 item = new sexp_list_item;
3136 item->set_data(str, t);
3139 // add a node to end of list, allocating memory for the text
3140 // Defaults: t = SEXPT_STRING
3142 void sexp_list_item::add_data_dup(char *str, int t)
3144 sexp_list_item *item, *ptr;
3146 item = new sexp_list_item;
3152 item->set_data(strdup(str), t);
3153 item->flags |= SEXP_ITEM_F_DUP;
3156 // add an sexp list to end of another list (join lists)
3158 void sexp_list_item::add_list(sexp_list_item *list)
3160 sexp_list_item *ptr;
3169 // free all nodes of list
3171 void sexp_list_item::destroy()
3173 sexp_list_item *ptr, *ptr2;
3178 if (ptr->flags & SEXP_ITEM_F_DUP)
3186 int sexp_tree::add_default_operator(int op, int argnum)
3190 sexp_list_item item;
3196 if (get_default_value(&item, op, argnum))
3199 if (item.type & SEXPT_OPERATOR) {
3200 Assert((item.op >= 0) && (item.op < Num_operators));
3201 add_or_replace_operator(item.op);
3206 // special case for modify-variable (data added 1st arg is variable)
3207 if ( !stricmp(Operators[op].text, "modify-variable") ) {
3210 int sexp_var_index = get_index_sexp_variable_name(item.text);
3211 Assert(sexp_var_index != -1);
3212 int type = SEXPT_VALID | SEXPT_VARIABLE;
3213 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
3214 type |= SEXPT_STRING;
3215 } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
3216 type |= SEXPT_NUMBER;
3221 char node_text[2*TOKEN_LENGTH + 2];
3222 sprintf(node_text, "%s(%s)", item.text, Sexp_variables[sexp_var_index].text);
3223 add_variable_data(node_text, type);
3225 // the the variable name
3227 Assert(argnum == 1);
3228 sexp_list_item temp_item;
3229 temp_item.text = buf2;
3230 get_default_value(&temp_item, op, 0);
3231 int sexp_var_index = get_index_sexp_variable_name(temp_item.text);
3232 Assert(sexp_var_index != -1);
3234 // from name get type
3235 int temp_type = Sexp_variables[sexp_var_index].type;
3237 if (temp_type & SEXP_VARIABLE_NUMBER) {
3238 type = SEXPT_VALID | SEXPT_NUMBER;
3239 } else if (temp_type & SEXP_VARIABLE_STRING) {
3240 type = SEXPT_VALID | SEXPT_STRING;
3244 add_data(item.text, type);
3247 add_data(item.text, item.type);
3254 int sexp_tree::get_default_value(sexp_list_item *item, int op, int i)
3258 sexp_list_item *list;
3263 type = query_operator_argument_type(op, i);
3266 item->set_op(OP_NOP);
3270 item->set_op(OP_TRUE);
3276 // if the top level operators is an AI goal, and we are adding the last number required,
3277 // assume that this number is a priority and make it 89 instead of 1.
3278 if ((query_operator_return_type(op) == OPR_AI_GOAL) && (i == (Operators[op].min - 1)))
3279 item->set_data("89", (SEXPT_NUMBER | SEXPT_VALID));
3280 else if (((Operators[op].value == OP_HAS_DOCKED_DELAY) || (Operators[op].value == OP_HAS_UNDOCKED_DELAY)) && (i == 2))
3281 item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
3282 else if ( (Operators[op].value == OP_SHIP_TYPE_DESTROYED) || (Operators[op].value == OP_GOOD_SECONDARY_TIME) )
3283 item->set_data("100", (SEXPT_NUMBER | SEXPT_VALID));
3285 item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
3290 list = get_listing_opf(type, index, i);
3297 strcpy(item->text, list->text);
3303 // catch anything that doesn't have a default value. Just describe what should be here instead
3306 case OPF_SHIP_NOT_PLAYER:
3308 case OPF_SHIP_POINT:
3309 case OPF_SHIP_WING_POINT:
3310 str = "<name of ship here>";
3314 str = "<name of wing here>";
3317 case OPF_DOCKER_POINT:
3318 str = "<docker point>";
3321 case OPF_DOCKEE_POINT:
3322 str = "<dockee point>";
3326 case OPF_AWACS_SUBSYSTEM:
3327 str = "<name of subsystem>";
3339 //str = "<any allied>";
3340 str = "<any wingman>";
3343 case OPF_WAYPOINT_PATH:
3344 str = "<waypoint path>";
3347 case OPF_MISSION_NAME:
3348 str = "<mission name>";
3352 str = "<goal name>";
3356 str = "<ship type here>";
3359 case OPF_EVENT_NAME:
3360 str = "<event name>";
3363 case OPF_HUGE_WEAPON:
3364 str = "<huge weapon type>";
3367 case OPF_JUMP_NODE_NAME:
3368 str = "<Jump node name>";
3372 str = "<new default required!>";
3376 item->set_data(str, (SEXPT_STRING | SEXPT_VALID));
3380 int sexp_tree::query_default_argument_available(int op)
3385 for (i=0; i<Operators[op].min; i++)
3386 if (!query_default_argument_available(op, i))
3392 int sexp_tree::query_default_argument_available(int op, int i)
3397 type = query_operator_argument_type(op, i);
3408 case OPF_AWACS_SUBSYSTEM:
3409 case OPF_DOCKER_POINT:
3410 case OPF_DOCKEE_POINT:
3414 case OPF_SKILL_LEVEL:
3415 case OPF_MEDAL_NAME:
3416 case OPF_WEAPON_NAME:
3417 case OPF_SHIP_CLASS_NAME:
3418 case OPF_HUD_GAUGE_NAME:
3419 case OPF_HUGE_WEAPON:
3420 case OPF_JUMP_NODE_NAME:
3425 case OPF_SHIP_NOT_PLAYER:
3427 case OPF_SHIP_POINT:
3428 case OPF_SHIP_WING_POINT:
3429 ptr = GET_FIRST(&obj_used_list);
3430 while (ptr != END_OF_LIST(&obj_used_list)) {
3431 if (ptr->type == OBJ_SHIP)
3434 ptr = GET_NEXT(ptr);
3440 for (j=0; j<MAX_WINGS; j++)
3441 if (Wings[j].wave_count)
3447 case OPF_WAYPOINT_PATH:
3448 if (Num_waypoint_lists)
3453 case OPF_MISSION_NAME:
3454 if (m_mode != MODE_CAMPAIGN) {
3455 if (!(*Mission_filename))
3461 if (Campaign.num_missions > 0)
3466 case OPF_GOAL_NAME: {
3469 value = Operators[op].value;
3471 if (m_mode == MODE_CAMPAIGN)
3474 // need to be sure that previous-goal functions are available. (i.e. we are providing a default argument for them)
3475 else if ((value == OP_PREVIOUS_GOAL_TRUE) || (value == OP_PREVIOUS_GOAL_FALSE) || (value == OP_PREVIOUS_GOAL_INCOMPLETE) || Num_goals)
3481 case OPF_EVENT_NAME: {
3484 value = Operators[op].value;
3485 if (m_mode == MODE_CAMPAIGN)
3488 // need to be sure that previous-event functions are available. (i.e. we are providing a default argument for them)
3489 else if ((value == OP_PREVIOUS_EVENT_TRUE) || (value == OP_PREVIOUS_EVENT_FALSE) || (value == OP_PREVIOUS_EVENT_INCOMPLETE) || Num_mission_events)
3496 if (m_mode == MODE_EVENTS) {
3497 Assert(Event_editor_dlg);
3498 if (Event_editor_dlg->current_message_name(0))
3502 if (Num_messages > Num_builtin_messages)
3508 case OPF_VARIABLE_NAME:
3509 if (sexp_variable_count() > 0) {
3523 // expand a combined line (one with an operator and it's one argument on the same line) into
3525 void sexp_tree::expand_operator(int node)
3530 if (nodes[node].flags & COMBINED) {
3531 node = nodes[node].parent;
3532 Assert((nodes[node].flags & OPERAND) && (nodes[node].flags & EDITABLE));
3535 if ((nodes[node].flags & OPERAND) && (nodes[node].flags & EDITABLE)) { // expandable?
3536 Assert(nodes[node].type & SEXPT_OPERATOR);
3537 h = nodes[node].handle;
3538 data = nodes[node].child;
3539 Assert(data != -1 && nodes[data].next == -1 && nodes[data].child == -1);
3541 SetItem(h, TVIF_TEXT, nodes[node].text, 0, 0, 0, 0, 0);
3542 nodes[node].flags = OPERAND;
3543 nodes[data].handle = insert(nodes[data].text, BITMAP_DATA, BITMAP_DATA, h);
3544 nodes[data].flags = EDITABLE;
3545 Expand(h, TVE_EXPAND);
3549 // expand a CTreeCtrl branch and all of it's children
3550 void sexp_tree::expand_branch(HTREEITEM h)
3552 Expand(h, TVE_EXPAND);
3553 h = GetChildItem(h);
3556 h = GetNextSiblingItem(h);
3560 void sexp_tree::merge_operator(int node)
3565 if (nodes[node].flags == EDITABLE) // data
3566 node = nodes[node].parent;
3569 child = nodes[node].child;
3570 if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) {
3571 sprintf(buf, "%s %s", nodes[node].text, nodes[child].text);
3572 SetItemText(nodes[node].handle, buf);
3573 nodes[node].flags = OPERAND | EDITABLE;
3574 nodes[child].flags = COMBINED;
3575 DeleteItem(nodes[child].handle);
3576 nodes[child].handle = NULL;
3582 // add a data node under operator pointed to by item_index
3583 int sexp_tree::add_data(char *data, int type)
3587 expand_operator(item_index);
3588 node = allocate_node(item_index);
3589 set_node(node, type, data);
3590 nodes[node].handle = insert(data, BITMAP_DATA, BITMAP_DATA, nodes[item_index].handle);
3591 nodes[node].flags = EDITABLE;
3596 // add a (variable) data node under operator pointed to by item_index
3597 int sexp_tree::add_variable_data(char *data, int type)
3601 Assert(type & SEXPT_VARIABLE);
3603 expand_operator(item_index);
3604 node = allocate_node(item_index);
3605 set_node(node, type, data);
3606 nodes[node].handle = insert(data, BITMAP_VARIABLE, BITMAP_VARIABLE, nodes[item_index].handle);
3607 nodes[node].flags = NOT_EDITABLE;
3612 // add an operator under operator pointed to by item_index. Updates item_index to point
3613 // to this new operator.
3614 void sexp_tree::add_operator(char *op, HTREEITEM h)
3618 if (item_index == -1) {
3619 node = allocate_node(-1);
3620 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
3621 item_handle = nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, h);
3624 expand_operator(item_index);
3625 node = allocate_node(item_index);
3626 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
3627 item_handle = nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, nodes[item_index].handle);
3630 nodes[node].flags = OPERAND;
3635 // add an operator with one argument under operator pointed to by item_index. This function
3636 // exists because the one arg case is a special case. The operator and argument is
3637 // displayed on the same line.
3638 /*void sexp_tree::add_one_arg_operator(char *op, char *data, int type)
3643 expand_operator(item_index);
3644 node1 = allocate_node(item_index);
3645 node2 = allocate_node(node1);
3646 set_node(node1, SEXPT_OPERATOR, op);
3647 set_node(node2, type, data);
3648 sprintf(str, "%s %s", op, data);
3649 nodes[node1].handle = insert(str, nodes[item_index].handle);
3650 nodes[node1].flags = OPERAND | EDITABLE;
3651 nodes[node2].flags = COMBINED;
3656 int sexp_tree::verify_tree(int *bypass)
3658 return verify_tree(0, bypass);
3661 // check the sexp tree for errors. Return -1 if error, or 0 if no errors. If an error
3662 // is found, item_index = node of error.
3663 int sexp_tree::verify_tree(int node, int *bypass)
3665 int i, type, count, op, type2, op2, argnum = 0;
3668 return 0; // nothing to check
3670 Assert(node >= 0 && node < MAX_SEXP_TREE_SIZE);
3671 Assert(nodes[node].type == SEXPT_OPERATOR);
3673 op = identify_operator(nodes[node].text);
3675 return node_error(node, "Unknown operator", bypass);
3677 count = count_args(nodes[node].child);
3678 if (count < Operators[op].min)
3679 return node_error(node, "Too few arguments for operator", bypass);
3680 if (count > Operators[op].max)
3681 return node_error(node, "Too many arguments for operator", bypass);
3683 node = nodes[node].child; // get first argument
3684 while (node != -1) {
3685 type = query_operator_argument_type(op, argnum);
3686 Assert(nodes[node].type & SEXPT_VALID);
3687 if (nodes[node].type == SEXPT_OPERATOR) {
3688 if (verify_tree(node) == -1)
3691 op2 = identify_operator(nodes[node].text); // no error checking, because it was done in the call above.
3692 type2 = query_operator_return_type(op2);
3694 } else if (nodes[node].type == SEXPT_NUMBER) {
3698 ptr = nodes[node].text;
3700 if (!isdigit(*ptr++))
3701 return node_error(node, "Number is invalid", bypass);
3703 } else if (nodes[node].type == SEXPT_STRING) {
3704 type2 = SEXP_ATOM_STRING;
3707 Assert(0); // unknown and invalid sexp node type.
3711 if (type2 != OPR_NUMBER)
3712 return node_error(node, "Number or number return type expected here", bypass);
3717 if (type2 == SEXP_ATOM_STRING)
3718 if (ship_name_lookup(nodes[node].text) == -1)
3721 if (type2 != SEXP_ATOM_STRING)
3722 return node_error(node, "Ship name expected here", bypass);
3727 if (type2 == SEXP_ATOM_STRING)
3728 if (wing_name_lookup(nodes[node].text) == -1)
3731 if (type2 != SEXP_ATOM_STRING)
3732 return node_error(node, "Wing name expected here", bypass);
3737 if (type2 == SEXP_ATOM_STRING)
3738 if (ship_name_lookup(nodes[node].text) == -1)
3739 if (wing_name_lookup(nodes[node].text) == -1)
3742 if (type2 != SEXP_ATOM_STRING)
3743 return node_error(node, "Ship or wing name expected here", bypass);
3748 if (type2 != OPR_BOOL)
3749 return node_error(node, "Boolean return type expected here", bypass);
3754 if (type2 != OPR_NULL)
3755 return node_error(node, "No return type operator expected here", bypass);
3760 if (type2 != SEXP_ATOM_STRING || verify_vector(nodes[node].text))
3761 return node_error(node, "3d coordinate expected here", bypass);
3766 if (type2 == SEXP_ATOM_STRING)
3767 if (ai_get_subsystem_type(nodes[node].text) == SUBSYSTEM_UNKNOWN)
3770 if (type2 != SEXP_ATOM_STRING)
3771 return node_error(node, "Subsystem name expected here", bypass);
3776 if (type2 == SEXP_ATOM_STRING) {
3777 for (i=0; i<Num_team_names; i++)
3778 if (!stricmp(Team_names[i], nodes[node].text))
3782 if (i == Num_team_names)
3783 return node_error(node, "Iff team type expected here", bypass);
3788 if (type2 != OPR_AI_GOAL)
3789 return node_error(node, "Ai goal return type expected here", bypass);
3793 case OPF_DOCKER_POINT:
3794 if (type2 != SEXP_ATOM_STRING)
3795 return node_error(node, "Docker docking point name expected here", bypass);
3799 case OPF_DOCKEE_POINT:
3800 if (type2 != SEXP_ATOM_STRING)
3801 return node_error(node, "Dockee docking point name expected here", bypass);
3806 node = nodes[node].next;
3814 // display an error message and position to point of error (a node)
3815 int sexp_tree::node_error(int node, char *msg, int *bypass)
3823 item_handle = nodes[node].handle;
3824 if (nodes[node].flags & COMBINED)
3825 item_handle = nodes[nodes[node].parent].handle;
3827 ensure_visible(node);
3828 SelectItem(item_handle);
3829 sprintf(text, "%s\n\nContinue checking for more errors?", msg);
3830 if (MessageBox(text, "Sexp error", MB_YESNO | MB_ICONEXCLAMATION) == IDNO)
3836 void sexp_tree::hilite_item(int node)
3838 ensure_visible(node);
3839 SelectItem(nodes[node].handle);
3842 // because the MFC function EnsureVisible() doesn't do what it says it does, I wrote this.
3843 void sexp_tree::ensure_visible(int node)
3846 if (nodes[node].parent != -1)
3847 ensure_visible(nodes[node].parent); // expand all parents first
3849 if (nodes[node].child != -1) // expandable?
3850 Expand(nodes[node].handle, TVE_EXPAND); // expand this item
3853 void sexp_tree::link_modified(int *ptr)
3858 void get_variable_default_text_from_variable_text(char *text, char *default_text)
3864 start = strstr(text, "(");
3868 // get length and copy all but last char ")"
3869 len = strlen(start);
3870 strncpy(default_text, start, len-1);
3872 // add null termination
3873 default_text[len-1] = '\0';
3876 void get_variable_name_from_sexp_tree_node_text(const char *text, char *var_name)
3879 length = strcspn(text, "(");
3881 strncpy(var_name, text, length);
3882 var_name[length] = '\0';
3885 int sexp_tree::get_modify_variable_type()
3887 Assert(item_index > -1);
3891 int parent = nodes[item_index].parent;
3892 Assert(parent != -1);
3894 if ( !stricmp(nodes[parent].text, "modify-variable") ) {
3895 Assert(nodes[parent].child != -1);
3896 sexp_var_index = get_tree_name_to_sexp_variable_index(nodes[nodes[parent].child].text);
3897 Assert(sexp_var_index != -1);
3899 Int3(); // should not be called otherwise
3902 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
3904 } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
3905 return OPF_AMBIGUOUS;
3913 void sexp_tree::verify_and_fix_arguments(int node)
3915 int op, arg_num, type, tmp;
3916 static int flag = 0;
3917 sexp_list_item *list, *ptr;
3923 op = identify_operator(nodes[node].text);
3931 item_index = nodes[node].child;
3932 while (item_index >= 0) {
3933 // get listing of valid argument values for node item_index
3934 type = query_operator_argument_type(op, arg_num);
3935 // special case for modify-variable
3936 if (type == OPF_AMBIGUOUS) {
3937 // check if parent variable type is number, returns OPF_NUMBER or OPF_AMBIGUOUS
3938 type = get_modify_variable_type();
3940 if (query_restricted_opf_range(type)) {
3941 list = get_listing_opf(type, node, arg_num);
3942 if (!list && (arg_num >= Operators[op].min)) {
3943 free_node(item_index, 1);
3950 // get a pointer to nodes[item_index].text for normal value
3951 // or default variable value if variable
3953 char default_variable_text[TOKEN_LENGTH];
3954 if (nodes[item_index].type & SEXPT_VARIABLE) {
3955 // special case for modify-variable
3956 if ( !stricmp(Operators[op].text, "modify-variable") ) {
3957 // make text_ptr to start - before '('
3958 get_variable_name_from_sexp_tree_node_text(nodes[item_index].text, default_variable_text);
3959 text_ptr = default_variable_text;
3961 get_variable_default_text_from_variable_text(nodes[item_index].text, default_variable_text);
3962 text_ptr = default_variable_text;
3965 text_ptr = nodes[item_index].text;
3971 if (ptr->text != NULL) {
3972 // make sure text is not NULL
3973 // check that proposed text is valid for operator
3974 if ( !stricmp(ptr->text, text_ptr) )
3979 // text is NULL, so set ptr to NULL to end loop
3984 if (!ptr) { // argument isn't in list of valid choices,
3985 if (list->op >= 0) {
3986 replace_operator(list->text);
3988 replace_data(list->text, list->type);
3993 bool invalid = false;
3994 if (type == OPF_AMBIGUOUS) {
3995 if (SEXPT_TYPE(nodes[item_index].type) == SEXPT_OPERATOR) {
3999 if (SEXPT_TYPE(nodes[item_index].type) != SEXPT_OPERATOR) {
4005 replace_data("<Invalid>", (SEXPT_STRING | SEXPT_VALID));
4009 if (nodes[item_index].type & SEXPT_OPERATOR)
4010 verify_and_fix_arguments(item_index);
4013 item_index = nodes[item_index].next;
4021 void sexp_tree::replace_data(char *data, int type)
4026 node = nodes[item_index].child;
4030 nodes[item_index].child = -1;
4031 h = nodes[item_index].handle;
4032 while (ItemHasChildren(h))
4033 DeleteItem(GetChildItem(h));
4035 set_node(item_index, type, data);
4036 SetItemText(h, data);
4037 SetItemImage(h, BITMAP_DATA, BITMAP_DATA);
4038 nodes[item_index].flags = EDITABLE;
4040 // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
4041 verify_and_fix_arguments(nodes[item_index].parent);
4044 update_help(GetSelectedItem());
4048 // Replaces data with sexp_variable type data
4049 void sexp_tree::replace_variable_data(int var_idx, int type)
4055 Assert(type & SEXPT_VARIABLE);
4057 node = nodes[item_index].child;
4061 nodes[item_index].child = -1;
4062 h = nodes[item_index].handle;
4063 while (ItemHasChildren(h)) {
4064 DeleteItem(GetChildItem(h));
4068 sprintf(buf, "%s(%s)", Sexp_variables[var_idx].variable_name, Sexp_variables[var_idx].text);
4070 set_node(item_index, type, buf);
4071 SetItemText(h, buf);
4072 SetItemImage(h, BITMAP_VARIABLE, BITMAP_VARIABLE);
4073 nodes[item_index].flags = NOT_EDITABLE;
4075 // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
4076 verify_and_fix_arguments(nodes[item_index].parent);
4079 update_help(GetSelectedItem());
4084 void sexp_tree::replace_operator(char *op)
4089 node = nodes[item_index].child;
4093 nodes[item_index].child = -1;
4094 h = nodes[item_index].handle;
4095 while (ItemHasChildren(h))
4096 DeleteItem(GetChildItem(h));
4098 set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), op);
4100 nodes[item_index].flags = OPERAND;
4102 update_help(GetSelectedItem());
4104 // hack added at Allender's request. If changing ship in an ai-dock operator, re-default
4108 /*void sexp_tree::replace_one_arg_operator(char *op, char *data, int type)
4114 node = nodes[item_index].child;
4118 nodes[item_index].child = -1;
4119 h = nodes[item_index].handle;
4120 while (ItemHasChildren(h))
4121 DeleteItem(GetChildItem(h));
4123 node = allocate_node(item_index);
4124 set_node(item_index, SEXPT_OPERATOR, op);
4125 set_node(node, type, data);
4126 sprintf(str, "%s %s", op, data);
4127 SetItemText(h, str);
4128 nodes[item_index].flags = OPERAND | EDITABLE;
4129 nodes[node].flags = COMBINED;
4131 update_help(GetSelectedItem());
4134 // moves a whole sexp tree branch to a new position under 'parent' and after 'after'.
4135 // The expansion state is preserved, and node handles are updated.
4136 void sexp_tree::move_branch(int source, int parent)
4140 // if no source, skip everything
4142 node = nodes[source].parent;
4144 if (nodes[node].child == source)
4145 nodes[node].child = nodes[source].next;
4147 node = nodes[node].child;
4148 while (nodes[node].next != source) {
4149 node = nodes[node].next;
4153 nodes[node].next = nodes[source].next;
4157 nodes[source].parent = parent;
4158 nodes[source].next = -1;
4160 if (nodes[parent].child == -1)
4161 nodes[parent].child = source;
4163 node = nodes[parent].child;
4164 while (nodes[node].next != -1)
4165 node = nodes[node].next;
4167 nodes[node].next = source;
4170 move_branch(nodes[source].handle, nodes[parent].handle);
4173 move_branch(nodes[source].handle);
4177 HTREEITEM sexp_tree::move_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after)
4179 int i, image1, image2;
4180 HTREEITEM h = 0, child, next;
4183 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4184 if (nodes[i].handle == source)
4187 if (i < MAX_SEXP_TREE_SIZE) {
4188 GetItemImage(source, image1, image2);
4189 h = insert(GetItemText(source), image1, image2, parent, after);
4190 nodes[i].handle = h;
4193 GetItemImage(source, image1, image2);
4194 h = insert(GetItemText(source), image1, image2, parent, after);
4197 SetItemData(h, GetItemData(source));
4198 child = GetChildItem(source);
4200 next = GetNextSiblingItem(child);
4201 move_branch(child, h);
4205 if (GetItemState(source, TVIS_EXPANDED) & TVIS_EXPANDED)
4206 Expand(h, TVE_EXPAND);
4214 void sexp_tree::copy_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after)
4216 int i, image1, image2;
4220 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4221 if (nodes[i].handle == source)
4224 if (i < MAX_SEXP_TREE_SIZE) {
4225 GetItemImage(source, image1, image2);
4226 h = insert(GetItemText(source), image1, image2, parent, after);
4227 nodes[i].handle = h;
4230 GetItemImage(source, image1, image2);
4231 h = insert(GetItemText(source), image1, image2, parent, after);
4234 SetItemData(h, GetItemData(source));
4235 child = GetChildItem(source);
4237 copy_branch(child, h);
4238 child = GetNextSiblingItem(child);
4241 if (GetItemState(source, TVIS_EXPANDED) & TVIS_EXPANDED)
4242 Expand(h, TVE_EXPAND);
4246 void sexp_tree::swap_roots(HTREEITEM one, HTREEITEM two)
4250 Assert(!GetParentItem(one));
4251 Assert(!GetParentItem(two));
4252 // copy_branch(one, TVI_ROOT, two);
4253 // move_branch(two, TVI_ROOT, one);
4255 h = move_branch(one, TVI_ROOT, two);
4261 void sexp_tree::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
4265 // ScreenToClient(&m_pt);
4266 ASSERT(!m_dragging);
4267 m_h_drag = HitTest(m_pt, &flags);
4270 if (!m_mode || GetParentItem(m_h_drag))
4273 ASSERT(m_p_image_list == NULL);
4274 m_p_image_list = CreateDragImage(m_h_drag); // get the image list for dragging
4275 if (!m_p_image_list)
4278 m_p_image_list->DragShowNolock(TRUE);
4279 m_p_image_list->SetDragCursorImage(0, CPoint(0, 0));
4280 m_p_image_list->BeginDrag(0, CPoint(0,0));
4281 m_p_image_list->DragMove(m_pt);
4282 m_p_image_list->DragEnter(this, m_pt);
4287 void sexp_tree::OnLButtonDown(UINT nFlags, CPoint point)
4290 CTreeCtrl::OnLButtonDown(nFlags, point);
4293 void sexp_tree::OnMouseMove(UINT nFlags, CPoint point)
4299 ASSERT(m_p_image_list != NULL);
4300 m_p_image_list->DragMove(point);
4301 if ((hitem = HitTest(point, &flags)) != NULL)
4302 if (!GetParentItem(hitem)) {
4303 m_p_image_list->DragLeave(this);
4304 SelectDropTarget(hitem);
4306 m_p_image_list->DragEnter(this, point);
4310 CTreeCtrl::OnMouseMove(nFlags, point);
4313 void sexp_tree::OnLButtonUp(UINT nFlags, CPoint point)
4318 ASSERT(m_p_image_list != NULL);
4319 m_p_image_list->DragLeave(this);
4320 m_p_image_list->EndDrag();
4321 delete m_p_image_list;
4322 m_p_image_list = NULL;
4324 if (m_h_drop && m_h_drag != m_h_drop) {
4326 index1 = GetItemData(m_h_drag);
4327 index2 = GetItemData(m_h_drop);
4328 swap_roots(m_h_drag, m_h_drop);
4329 if (m_mode == MODE_GOALS) {
4330 Assert(Goal_editor_dlg);
4331 Goal_editor_dlg->swap_handler(index1, index2);
4333 } else if (m_mode == MODE_EVENTS) {
4334 Assert(Event_editor_dlg);
4335 Event_editor_dlg->swap_handler(index1, index2);
4337 } else if (m_mode == MODE_CAMPAIGN) {
4338 Campaign_tree_formp->swap_handler(index1, index2);
4348 SelectDropTarget(NULL);
4351 CTreeCtrl::OnLButtonUp(nFlags, point);
4354 void sexp_tree::setup(CEdit *ptr)
4356 CImageList *pimagelist;
4361 int stops[2] = { 10, 30 };
4363 help_box -> SetTabStops(2, (LPINT) stops);
4366 pimagelist = GetImageList(TVSIL_NORMAL);
4368 pimagelist = new CImageList();
4369 pimagelist->Create(16, 16, TRUE/*bMask*/, 2, 9);
4371 bitmap.LoadBitmap(IDB_OPERATOR);
4372 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4373 bitmap.DeleteObject();
4375 bitmap.LoadBitmap(IDB_DATA);
4376 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4377 bitmap.DeleteObject();
4379 bitmap.LoadBitmap(IDB_VARIABLE);
4380 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4381 bitmap.DeleteObject();
4383 bitmap.LoadBitmap(IDB_ROOT);
4384 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4385 bitmap.DeleteObject();
4387 bitmap.LoadBitmap(IDB_ROOT_DIRECTIVE);
4388 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4389 bitmap.DeleteObject();
4391 bitmap.LoadBitmap(IDB_CHAINED);
4392 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4393 bitmap.DeleteObject();
4395 bitmap.LoadBitmap(IDB_CHAINED_DIRECTIVE);
4396 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4397 bitmap.DeleteObject();
4399 bitmap.LoadBitmap(IDB_GREEN_DOT);
4400 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4401 bitmap.DeleteObject();
4403 bitmap.LoadBitmap(IDB_BLACK_DOT);
4404 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4405 bitmap.DeleteObject();
4407 SetImageList(pimagelist, TVSIL_NORMAL);
4410 //#define BITMAP_OPERATOR 0
4411 //#define BITMAP_DATA 1
4412 //#define BITMAP_VARIABLE 2
4413 //#define BITMAP_ROOT 3
4414 //#define BITMAP_ROOT_DIRECTIVE 4
4415 //#define BITMAP_CHAIN 5
4416 //#define BITMAP_CHAIN_DIRECTIVE 6
4417 //#define BITMAP_GREEN_DOT 7
4418 //#define BITMAP_BLACK_DOT 8
4421 HTREEITEM sexp_tree::insert(LPCTSTR lpszItem, int image, int sel_image, HTREEITEM hParent, HTREEITEM hInsertAfter)
4423 return InsertItem(lpszItem, image, sel_image, hParent, hInsertAfter);
4427 void sexp_tree::OnDestroy()
4429 CImageList *pimagelist;
4431 pimagelist = GetImageList(TVSIL_NORMAL);
4433 pimagelist->DeleteImageList();
4437 CTreeCtrl::OnDestroy();
4440 HTREEITEM sexp_tree::handle(int node)
4442 return nodes[node].handle;
4445 char *sexp_tree::help(int code)
4449 i = sizeof(Sexp_help) / sizeof(sexp_help_struct);
4451 if (Sexp_help[i].id == code)
4456 return Sexp_help[i].help;
4461 // get type of item clicked on
4462 int sexp_tree::get_type(HTREEITEM h)
4466 // get index into sexp_tree
4467 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4468 if (nodes[i].handle == h)
4471 if ( (i >= MAX_SEXP_TREE_SIZE) ) {
4472 // Int3(); // This would be the root of the tree -- ie, event name
4476 return nodes[i].type;
4480 void sexp_tree::update_help(HTREEITEM h)
4483 int i, j, z, c, code;
4486 for (i=0; i<Num_operators; i++)
4487 for (j=0; j<Num_op_menus; j++)
4488 if ((Operators[i].value & OP_CATAGORY_MASK) == op_menu[j].id) {
4489 if (!help(Operators[i].value))
4490 Int3(); // Damn you, Allender! If you add new sexp operators, add help for them too! :)
4493 help_box = (CEdit *) GetParent()->GetDlgItem(IDC_HELP_BOX);
4494 if (!help_box || !::IsWindow(help_box->m_hWnd))
4497 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4498 if (nodes[i].handle == h)
4501 if ((i >= MAX_SEXP_TREE_SIZE) || !nodes[i].type) {
4502 help_box->SetWindowText("");
4506 if (SEXPT_TYPE(nodes[i].type) != SEXPT_OPERATOR) {
4507 z = nodes[i].parent;
4509 Warning(LOCATION, "Sexp data \"%s\" has no parent!", nodes[i].text);
4513 code = identify_operator(nodes[z].text);
4517 while ((j >= 0) && (j != i)) {
4523 if (query_operator_argument_type(code, c) == OPF_MESSAGE) {
4524 for (j=0; j<Num_messages; j++)
4525 if (!stricmp(Messages[j].name, nodes[i].text)) {
4526 text.Format("Message Text:\r\n%s", Messages[j].message);
4527 help_box->SetWindowText(text);
4536 code = find_operator(nodes[i].text);
4539 str = "No help available";
4541 help_box->SetWindowText(str);
4544 // find list of sexp_tree nodes with text
4545 // stuff node indices into find[]
4546 int sexp_tree::find_text(char *text, int *find)
4551 for (i=0; i<MAX_SEARCH_MESSAGE_DEPTH; i++) {
4557 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
4558 // only look at used and editable nodes
4559 if ((nodes[i].flags & EDITABLE && (nodes[i].type != SEXPT_UNUSED))) {
4561 if ( !stricmp(nodes[i].text, text) ) {
4562 find[find_count++] = i;
4564 // don't exceed max count - array bounds
4565 if (find_count == MAX_SEARCH_MESSAGE_DEPTH) {
4576 void sexp_tree::OnKeydown(NMHDR *pNMHDR, LRESULT *pResult)
4579 TV_KEYDOWN *pTVKeyDown = (TV_KEYDOWN *) pNMHDR;
4581 key = pTVKeyDown->wVKey;
4582 if (key == VK_SPACE)
4583 EditLabel(GetSelectedItem());
4588 // Determine if a given opf code has a restricted argument range (i.e. has a specific, limited
4589 // set of argument values, or has virtually unlimited possibilities. For example, boolean values
4590 // only have true or false, so it is restricted, but a number could be anything, so it's not.
4592 int sexp_tree::query_restricted_opf_range(int opf)
4604 // generate listing of valid argument values.
4605 // opf = operator format to generate list for
4606 // parent_node = the parent node we are generating list for
4607 // arg_index = argument number of parent this argument will go at
4609 sexp_list_item *sexp_tree::get_listing_opf(int opf, int parent_node, int arg_index)
4616 return get_listing_opf_null();
4619 return get_listing_opf_bool(parent_node);
4622 return get_listing_opf_number();
4625 return get_listing_opf_ship(parent_node);
4628 return get_listing_opf_wing();
4630 case OPF_AWACS_SUBSYSTEM:
4632 return get_listing_opf_subsystem(parent_node, arg_index);
4635 return get_listing_opf_point();
4638 return get_listing_opf_iff();
4641 return get_listing_opf_ai_goal(parent_node);
4643 case OPF_DOCKER_POINT:
4644 return get_listing_opf_docker_point(parent_node);
4646 case OPF_DOCKEE_POINT:
4647 return get_listing_opf_dockee_point(parent_node);
4650 return get_listing_opf_message();
4653 return get_listing_opf_who_from();
4656 return get_listing_opf_priority();
4658 case OPF_WAYPOINT_PATH:
4659 return get_listing_opf_waypoint_path();
4662 return get_listing_opf_positive();
4664 case OPF_MISSION_NAME:
4665 return get_listing_opf_mission_name();
4667 case OPF_SHIP_POINT:
4668 return get_listing_opf_ship_point();
4671 return get_listing_opf_goal_name(parent_node);
4674 return get_listing_opf_ship_wing();
4676 case OPF_SHIP_WING_POINT:
4677 return get_listing_opf_ship_wing_point();
4680 return get_listing_opf_ship_type();
4683 return get_listing_opf_keypress();
4685 case OPF_EVENT_NAME:
4686 return get_listing_opf_event_name(parent_node);
4689 return get_listing_opf_ai_order();
4691 case OPF_SKILL_LEVEL:
4692 return get_listing_opf_skill_level();
4694 case OPF_MEDAL_NAME:
4695 return get_listing_opf_medal_name();
4697 case OPF_WEAPON_NAME:
4698 return get_listing_opf_weapon_name();
4700 case OPF_SHIP_CLASS_NAME:
4701 return get_listing_opf_ship_class_name();
4703 case OPF_HUD_GAUGE_NAME:
4704 return get_listing_opf_hud_gauge_name();
4706 case OPF_HUGE_WEAPON:
4707 return get_listing_opf_huge_weapon();
4709 case OPF_SHIP_NOT_PLAYER:
4710 return get_listing_opf_ship_not_player();
4712 case OPF_JUMP_NODE_NAME:
4713 return get_listing_opf_jump_nodes();
4715 case OPF_VARIABLE_NAME:
4716 return get_listing_opf_variable_names();
4722 Int3(); // unknown OPF code
4728 sexp_list_item *sexp_tree::get_listing_opf_null()
4731 sexp_list_item head;
4733 for (i=0; i<Num_operators; i++)
4734 if (query_operator_return_type(i) == OPR_NULL)
4740 sexp_list_item *sexp_tree::get_listing_opf_bool(int parent_node)
4743 sexp_list_item head;
4745 // search for the previous goal/event operators. If found, only add the true/false
4746 // sexpressions to the list
4748 if ( parent_node != -1 ) {
4751 op = find_operator(nodes[parent_node].text);
4752 if ( (op == OP_PREVIOUS_GOAL_TRUE) || (op == OP_PREVIOUS_GOAL_FALSE) || (op == OP_PREVIOUS_EVENT_TRUE) || (op == OP_PREVIOUS_EVENT_FALSE) )
4757 for (i=0; i<Num_operators; i++) {
4758 if (query_operator_return_type(i) == OPR_BOOL) {
4759 if ( !only_basic || (only_basic && ((Operators[i].value == OP_TRUE) || (Operators[i].value == OP_FALSE))) ) {
4768 sexp_list_item *sexp_tree::get_listing_opf_positive()
4771 sexp_list_item head;
4773 for (i=0; i<Num_operators; i++)
4774 if (query_operator_return_type(i) == OPR_POSITIVE)
4780 sexp_list_item *sexp_tree::get_listing_opf_number()
4783 sexp_list_item head;
4785 for (i=0; i<Num_operators; i++) {
4786 z = query_operator_return_type(i);
4787 if ((z == OPR_NUMBER) || (z == OPR_POSITIVE))
4794 sexp_list_item *sexp_tree::get_listing_opf_ship(int parent_node)
4797 sexp_list_item head;
4798 int op = 0, dock_ship = -1, require_cap_ship = 0;
4800 // look at the parent node and get the operator. Some ship lists should be filtered based
4801 // on what the parent operator is
4802 if ( parent_node >= 0 ) {
4803 op = find_operator(nodes[parent_node].text);
4805 // prune out to only capital ships
4806 if (!stricmp(nodes[parent_node].text, "cap-subsys-cargo-known-delay")) {
4807 require_cap_ship = 1;
4810 // get the dock_ship number of if this goal is an ai dock goal. used to prune out unwanted ships out
4811 // of the generated ship list
4813 if ( op == OP_AI_DOCK ) {
4816 z = nodes[parent_node].parent;
4818 Assert(!stricmp(nodes[z].text, "add-ship-goal") || !stricmp(nodes[z].text, "add-wing-goal") || !stricmp(nodes[z].text, "add-goal"));
4823 dock_ship = ship_name_lookup(nodes[z].text, 1);
4824 Assert( dock_ship != -1 );
4828 ptr = GET_FIRST(&obj_used_list);
4829 while (ptr != END_OF_LIST(&obj_used_list)) {
4830 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
4831 if ( op == OP_AI_DOCK ) {
4832 // only include those ships in the list which the given ship can dock with.
4833 if ( (dock_ship != ptr->instance) && ship_docking_valid(dock_ship , ptr->instance) )
4834 head.add_data(Ships[ptr->instance].ship_name );
4837 if ( !require_cap_ship || (Ship_info[Ships[ptr->instance].ship_info_index].flags & SIF_HUGE_SHIP) ) {
4838 head.add_data(Ships[ptr->instance].ship_name);
4843 ptr = GET_NEXT(ptr);
4849 sexp_list_item *sexp_tree::get_listing_opf_wing()
4852 sexp_list_item head;
4854 for (i=0; i<MAX_WINGS; i++){
4855 if (Wings[i].wave_count){
4856 head.add_data(Wings[i].name);
4863 // specific types of subsystems we're looking for
4864 #define OPS_CAP_CARGO 1
4865 #define OPS_STRENGTH 2
4866 #define OPS_BEAM_TURRET 3
4868 sexp_list_item *sexp_tree::get_listing_opf_subsystem(int parent_node, int arg_index)
4871 int special_subsys = 0;
4872 sexp_list_item head;
4873 ship_subsys *subsys;
4875 // determine if the parent is one of the set subsystem strength items. If so,
4876 // we want to append the "Hull" name onto the end of the menu
4877 Assert(parent_node >= 0);
4879 // get the operator type of the node
4880 op = find_operator(nodes[parent_node].text);
4883 child = nodes[parent_node].child;
4887 // where we care about hull strength
4888 case OP_REPAIR_SUBSYSTEM:
4889 case OP_SABOTAGE_SUBSYSTEM:
4890 case OP_SET_SUBSYSTEM_STRNGTH:
4891 special_subsys = OPS_STRENGTH;
4895 case OP_AWACS_SET_RADIUS:
4896 special_subsys = OPS_AWACS;
4899 // where we care about capital ship subsystem cargo
4900 case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
4901 special_subsys = OPS_CAP_CARGO;
4903 // get the next sibling
4904 child = nodes[child].next;
4907 // where we care about turrets carrying beam weapons
4909 special_subsys = OPS_BEAM_TURRET;
4911 // if this is arg index 3 (targeted ship)
4913 Assert(arg_index == 3);
4914 child = nodes[child].next;
4916 child = nodes[child].next;
4918 Assert(arg_index == 1);
4923 // now find the ship and add all relevant subsystems
4925 sh = ship_name_lookup(nodes[child].text, 1);
4927 subsys = GET_FIRST(&Ships[sh].subsys_list);
4928 while (subsys != END_OF_LIST(&Ships[sh].subsys_list)) {
4930 switch(special_subsys){
4933 if (valid_cap_subsys_cargo_list(subsys->system_info->subobj_name) ) {
4934 head.add_data(subsys->system_info->subobj_name);
4939 case OPS_BEAM_TURRET:
4940 head.add_data(subsys->system_info->subobj_name);
4945 if (subsys->system_info->flags & MSS_FLAG_AWACS) {
4946 head.add_data(subsys->system_info->subobj_name);
4952 head.add_data(subsys->system_info->subobj_name);
4957 subsys = GET_NEXT(subsys);
4961 // if one of the subsystem strength operators, append the Hull string
4962 if(special_subsys == OPS_STRENGTH){
4963 head.add_data(SEXP_HULL_STRING);
4969 sexp_list_item *sexp_tree::get_listing_opf_point()
4971 char buf[NAME_LENGTH+8];
4973 sexp_list_item head;
4975 for (i=0; i<Num_waypoint_lists; i++)
4976 for (j=0; j<Waypoint_lists[i].count; j++) {
4977 sprintf(buf, "%s:%d", Waypoint_lists[i].name, j + 1);
4978 head.add_data_dup(buf);
4984 sexp_list_item *sexp_tree::get_listing_opf_iff()
4987 sexp_list_item head;
4989 for (i=0; i<Num_team_names; i++)
4990 head.add_data(Team_names[i]);
4995 sexp_list_item *sexp_tree::get_listing_opf_ai_goal(int parent_node)
4997 int i, n, w, z, child;
4998 sexp_list_item head;
5000 Assert(parent_node >= 0);
5001 child = nodes[parent_node].child;
5003 n = ship_name_lookup(nodes[child].text, 1);
5005 // add operators if it's an ai-goal and ai-goal is allowed for that ship
5006 for (i=0; i<Num_operators; i++) {
5007 if ( (query_operator_return_type(i) == OPR_AI_GOAL) && query_sexp_ai_goal_valid(Operators[i].value, n) )
5012 z = wing_name_lookup(nodes[child].text);
5014 for (w=0; w<Wings[z].wave_count; w++) {
5015 n = Wings[z].ship_index[w];
5016 // add operators if it's an ai-goal and ai-goal is allowed for that ship
5017 for (i=0; i<Num_operators; i++) {
5018 if ( (query_operator_return_type(i) == OPR_AI_GOAL) && query_sexp_ai_goal_valid(Operators[i].value, n) )
5024 return NULL; // no valid ship or wing to check against, make nothing available
5030 sexp_list_item *sexp_tree::get_listing_opf_docker_point(int parent_node)
5033 sexp_list_item head;
5035 Assert(parent_node >= 0);
5036 Assert(!stricmp(nodes[parent_node].text, "ai-dock"));
5038 z = nodes[parent_node].parent;
5040 Assert(!stricmp(nodes[z].text, "add-ship-goal") || !stricmp(nodes[z].text, "add-wing-goal") || !stricmp(nodes[z].text, "add-goal"));
5045 sh = ship_name_lookup(nodes[z].text, 1);
5047 z = get_docking_list(Ships[sh].modelnum);
5049 head.add_data(Docking_bay_list[i]);
5055 sexp_list_item *sexp_tree::get_listing_opf_dockee_point(int parent_node)
5058 sexp_list_item head;
5060 Assert(parent_node >= 0);
5061 Assert(!stricmp(nodes[parent_node].text, "ai-dock"));
5063 z = nodes[parent_node].child;
5066 sh = ship_name_lookup(nodes[z].text, 1);
5068 z = get_docking_list(Ships[sh].modelnum);
5070 head.add_data(Docking_bay_list[i]);
5076 sexp_list_item *sexp_tree::get_listing_opf_message()
5080 sexp_list_item head;
5082 if (m_mode == MODE_EVENTS) {
5083 Assert(Event_editor_dlg);
5084 // this for looks a litle strange, but had to do it get rid of a warning. Conditional
5085 //uses last statement is sequence, i.e. same as for (i=0, str, i++)
5086 for (i=0; str = Event_editor_dlg->current_message_name(i), str; i++)
5090 for (i=Num_builtin_messages; i<Num_messages; i++)
5091 head.add_data(Messages[i].name);
5097 sexp_list_item *sexp_tree::get_listing_opf_who_from()
5100 sexp_list_item head;
5102 //head.add_data("<any allied>");
5103 head.add_data("#Command");
5104 head.add_data("<any wingman>");
5106 ptr = GET_FIRST(&obj_used_list);
5107 while (ptr != END_OF_LIST(&obj_used_list)) {
5108 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START))
5109 if (!(Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].flags & SIF_NOT_FLYABLE))
5110 head.add_data(Ships[ptr->instance].ship_name);
5112 ptr = GET_NEXT(ptr);
5118 sexp_list_item *sexp_tree::get_listing_opf_priority()
5120 sexp_list_item head;
5122 head.add_data("High");
5123 head.add_data("Normal");
5124 head.add_data("Low");
5128 sexp_list_item *sexp_tree::get_listing_opf_waypoint_path()
5131 sexp_list_item head;
5133 for (i=0; i<Num_waypoint_lists; i++)
5134 head.add_data(Waypoint_lists[i].name);
5139 sexp_list_item *sexp_tree::get_listing_opf_ship_point()
5141 sexp_list_item head;
5143 head.add_list(get_listing_opf_ship());
5144 head.add_list(get_listing_opf_point());
5148 sexp_list_item *sexp_tree::get_listing_opf_ship_wing_point()
5150 sexp_list_item head;
5152 head.add_data("<any friendly>");
5153 head.add_data("<any hostile>");
5154 head.add_data("<any neutral>");
5155 head.add_data("<any unknown>");
5156 head.add_list(get_listing_opf_ship());
5157 head.add_list(get_listing_opf_wing());
5158 head.add_list(get_listing_opf_point());
5162 sexp_list_item *sexp_tree::get_listing_opf_mission_name()
5165 sexp_list_item head;
5167 if ((m_mode == MODE_CAMPAIGN) && (Cur_campaign_mission >= 0)) {
5168 for (i=0; i<Campaign.num_missions; i++)
5169 if ( (i == Cur_campaign_mission) || (Campaign.missions[i].level < Campaign.missions[Cur_campaign_mission].level) )
5170 head.add_data(Campaign.missions[i].name);
5173 head.add_data(Mission_filename);
5178 sexp_list_item *sexp_tree::get_listing_opf_goal_name(int parent_node)
5181 sexp_list_item head;
5183 if (m_mode == MODE_CAMPAIGN) {
5186 Assert(parent_node >= 0);
5187 child = nodes[parent_node].child;
5190 for (m=0; m<Campaign.num_missions; m++)
5191 if (!stricmp(Campaign.missions[m].name, nodes[child].text))
5194 if (m < Campaign.num_missions) {
5195 if (Campaign.missions[m].num_goals < 0) // haven't loaded goal names yet.
5196 read_mission_goal_list(m);
5198 for (i=0; i<Campaign.missions[m].num_goals; i++)
5199 head.add_data(Campaign.missions[m].goals[i].name);
5203 for (i=0; i<Num_goals; i++)
5204 head.add_data(Mission_goals[i].name);
5210 sexp_list_item *sexp_tree::get_listing_opf_ship_wing()
5212 sexp_list_item head;
5214 head.add_list(get_listing_opf_ship());
5215 head.add_list(get_listing_opf_wing());
5219 sexp_list_item *sexp_tree::get_listing_opf_ship_type()
5222 sexp_list_item head;
5224 for (i=0; i<MAX_SHIP_TYPE_COUNTS; i++){
5225 head.add_data(Ship_type_names[i]);
5231 sexp_list_item *sexp_tree::get_listing_opf_keypress()
5234 sexp_list_item head;
5236 for (i=0; i<CCFG_MAX; i++) {
5237 if (Control_config[i].key_default > 0) {
5238 head.add_data_dup(textify_scancode(Control_config[i].key_default));
5245 sexp_list_item *sexp_tree::get_listing_opf_event_name(int parent_node)
5248 sexp_list_item head;
5251 if (m_mode == MODE_CAMPAIGN) {
5254 Assert(parent_node >= 0);
5255 child = nodes[parent_node].child;
5258 for (m=0; m<Campaign.num_missions; m++)
5259 if (!stricmp(Campaign.missions[m].name, nodes[child].text))
5262 if (m < Campaign.num_missions) {
5263 if (Campaign.missions[m].num_events < 0) // haven't loaded goal names yet.
5264 read_mission_goal_list(m);
5266 for (i=0; i<Campaign.missions[m].num_events; i++)
5267 head.add_data(Campaign.missions[m].events[i].name);
5271 for (i=0; i<Num_mission_events; i++)
5272 head.add_data(Mission_events[i].name);
5278 sexp_list_item *sexp_tree::get_listing_opf_ai_order()
5281 sexp_list_item head;
5283 for (i=0; i<Fred_comm_orders_max; i++)
5284 head.add_data(Fred_comm_orders[i].menu_text);
5289 sexp_list_item *sexp_tree::get_listing_opf_skill_level()
5292 sexp_list_item head;
5294 for (i=0; i<NUM_SKILL_LEVELS; i++)
5295 head.add_data(Skill_level_names(i, 0));
5300 sexp_list_item *sexp_tree::get_listing_opf_medal_name()
5303 sexp_list_item head;
5305 for (i=0; i<MAX_ASSIGNABLE_MEDALS; i++)
5306 head.add_data(Medals[i].name);
5308 // also add SOC crest (index 17) and Wings (index 13)
5309 head.add_data(Medals[13].name);
5310 head.add_data(Medals[17].name);
5315 sexp_list_item *sexp_tree::get_listing_opf_weapon_name()
5318 sexp_list_item head;
5320 for (i=0; i<Num_weapon_types; i++)
5321 head.add_data(Weapon_info[i].name);
5326 sexp_list_item *sexp_tree::get_listing_opf_ship_class_name()
5329 sexp_list_item head;
5331 for (i=0; i<Num_ship_types; i++)
5332 head.add_data(Ship_info[i].name);
5337 sexp_list_item *sexp_tree::get_listing_opf_hud_gauge_name()
5340 sexp_list_item head;
5342 for (i=0; i<NUM_HUD_GAUGES; i++)
5343 head.add_data(HUD_gauge_text[i]);
5348 sexp_list_item *sexp_tree::get_listing_opf_huge_weapon()
5351 sexp_list_item head;
5353 for (i=0; i<Num_weapon_types; i++) {
5354 if (Weapon_info[i].wi_flags & WIF_HUGE)
5355 head.add_data(Weapon_info[i].name);
5361 sexp_list_item *sexp_tree::get_listing_opf_ship_not_player()
5364 sexp_list_item head;
5366 ptr = GET_FIRST(&obj_used_list);
5367 while (ptr != END_OF_LIST(&obj_used_list)) {
5368 if (ptr->type == OBJ_SHIP)
5369 head.add_data(Ships[ptr->instance].ship_name);
5371 ptr = GET_NEXT(ptr);
5377 sexp_list_item *sexp_tree::get_listing_opf_jump_nodes()
5380 sexp_list_item head;
5382 for (i = 0; i < Num_jump_nodes; i++ )
5383 head.add_data( Jump_nodes[i].name );
5388 // creates list of Sexp_variables
5389 sexp_list_item *sexp_tree::get_listing_opf_variable_names()
5392 sexp_list_item head;
5394 for (i=0; i<MAX_SEXP_VARIABLES; i++) {
5395 if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
5396 head.add_data( Sexp_variables[i].variable_name );
5404 // Deletes sexp_variable from sexp_tree.
5405 // resets tree to not include given variable, and resets text and type
5406 void sexp_tree::delete_sexp_tree_variable(const char *var_name)
5408 char search_str[64];
5409 char replace_text[TOKEN_LENGTH];
5411 sprintf(search_str, "%s(", var_name);
5413 // store old item index
5414 int old_item_index = item_index;
5416 for (int idx=0; idx<MAX_SEXP_TREE_SIZE; idx++) {
5417 if (nodes[idx].type & SEXPT_VARIABLE) {
5418 if ( strstr(nodes[idx].text, search_str) != NULL ) {
5420 // check type is number or string
5421 Assert( (nodes[idx].type & SEXPT_NUMBER) || (nodes[idx].type & SEXPT_STRING) );
5423 // reset type as not variable
5424 int type = nodes[idx].type &= ~SEXPT_VARIABLE;
5427 if (nodes[idx].type & SEXPT_NUMBER) {
5428 strcpy(replace_text, "number");
5430 strcpy(replace_text, "string");
5433 // set item_index and replace data
5435 replace_data(replace_text, type);
5440 // restore item_index
5441 item_index = old_item_index;
5445 // Modify sexp_tree for a change in sexp_variable (name, type, or default value)
5446 void sexp_tree::modify_sexp_tree_variable(const char *old_name, int sexp_var_index)
5448 char search_str[64];
5451 Assert(Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_SET);
5452 Assert( (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) || (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) );
5454 // Get type for sexp_tree node
5455 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
5456 type = (SEXPT_NUMBER | SEXPT_VALID);
5458 type = (SEXPT_STRING | SEXPT_VALID);
5461 // store item index;
5462 int old_item_index = item_index;
5464 // Search string in sexp_tree nodes
5465 sprintf(search_str, "%s(", old_name);
5467 for (int idx=0; idx<MAX_SEXP_TREE_SIZE; idx++) {
5468 if (nodes[idx].type & SEXPT_VARIABLE) {
5469 if ( strstr(nodes[idx].text, search_str) != NULL ) {
5470 // temp set item_index
5473 // replace variable data
5474 replace_variable_data(sexp_var_index, (type | SEXPT_VARIABLE));
5479 // restore item_index
5480 item_index = old_item_index;
5484 // convert from item_index to sexp_variable index, -1 if not
5485 int sexp_tree::get_item_index_to_var_index()
5487 // check valid item index and node is a variable
5488 if ( (item_index > 0) && (nodes[item_index].type & SEXPT_VARIABLE) ) {
5490 return get_tree_name_to_sexp_variable_index(nodes[item_index].text);
5496 int sexp_tree::get_tree_name_to_sexp_variable_index(const char *tree_name)
5498 char var_name[TOKEN_LENGTH];
5500 int chars_to_copy = strcspn(tree_name, "(");
5501 Assert(chars_to_copy < TOKEN_LENGTH - 1);
5503 // Copy up to '(' and add null termination
5504 strncpy(var_name, tree_name, chars_to_copy);
5505 var_name[chars_to_copy] = '\0';
5508 return get_index_sexp_variable_name(var_name);
5511 int sexp_tree::get_variable_count(const char *var_name)
5515 char compare_name[64];
5517 // get name to compare
5518 strcpy(compare_name, var_name);
5519 strcat(compare_name, "(");
5521 // look for compare name
5522 for (idx=0; idx<MAX_SEXP_TREE_SIZE; idx++) {
5523 if (nodes[idx].type & SEXPT_VARIABLE) {
5524 if ( strstr(nodes[idx].text, compare_name) ) {