2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Fred2/Sexp_tree.cpp $
15 * Sexp tree handler class. Almost everything is handled by this class.
18 * Revision 1.3 2002/06/09 04:41:16 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:44 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:08 root
28 * 43 9/07/99 9:22p Jefff
29 * added 2 more assignable medals
31 * 42 9/07/99 1:05a Andsager
32 * Added team-score sexp for multi team vs team missions
34 * 41 8/27/99 4:07p Andsager
35 * Add is-ship-visible sexp. Make ship-vanish sexp SINGLE player only
37 * 40 8/24/99 4:25p Andsager
38 * Add ship-vanish sexp
40 * 39 8/16/99 10:04p Andsager
41 * Add special-warp-dist and special-warpout-name sexp for Knossos device
44 * 38 8/09/99 2:00p Dave
47 * 37 8/02/99 4:26p Dave
48 * Added 2 new sexpressions.
50 * 36 8/02/99 1:43p Andsager
53 * 35 7/28/99 1:36p Andsager
54 * Modify cargo1 to include flag CARGO_NO_DEPLETE. Add sexp
55 * cargo-no-deplete (only for BIG / HUGE). Modify ship struct to pack
58 * 34 7/24/99 4:56p Dave
59 * Added 3 new sexpressions.
61 * 33 7/21/99 8:10p Dave
62 * First run of supernova effect.
64 * 32 7/20/99 9:19p Andsager
65 * Added facing waypoint sexp
67 * 31 7/20/99 9:54a Andsager
68 * Add subsys-set-random sexp
70 * 30 7/19/99 12:02p Andsager
71 * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
72 * only blow up subsystem if its strength is > 0
74 * 29 7/13/99 3:37p Andsager
75 * Add secondaries-depleted sexp
77 * 28 7/12/99 12:01p Andsager
78 * Make message by default come from command.
80 * 27 7/08/99 12:06p Andsager
81 * Add turret-tagged-only and turret-tagged-clear sexp.
83 * 26 6/29/99 10:08a Andsager
86 * 25 6/23/99 5:51p Andsager
87 * Add waypoint-cap-speed. Checkin stealth ai - inactive.
89 * 24 6/16/99 10:21a Dave
90 * Added send-message-list sexpression.
92 * 23 6/01/99 8:35p Dave
93 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
94 * awacs-set-radius sexpression.
96 * 22 5/24/99 11:28a Dave
97 * Sexpression for adding/removing ships from the hud escort list.
99 * 21 5/20/99 1:40p Andsager
100 * Fix find_text() to only look at nodes that are used.
102 * 20 5/04/99 5:21p Andsager
104 * 19 4/28/99 9:33a Andsager
105 * Add turret-free and turret-lock (and -all) sexp. Stargger start time
106 * of beam weapons beam-free and beam-free-all.
108 * 18 4/26/99 2:14p Andsager
109 * Add beam-protect-ship and beam-unprotect-ship sexp.
111 * 17 4/23/99 12:01p Johnson
112 * Added SIF_HUGE_SHIP
114 * 16 4/02/99 9:54a Dave
115 * Added a few more options in the weapons.tbl for beam weapons. Attempt
116 * at putting "pain" packets into multiplayer.
118 * 15 3/20/99 3:46p Dave
119 * Added support for model-based background nebulae. Added 3 new
122 * 14 3/04/99 6:09p Dave
123 * Added in sexpressions for firing beams and checking for if a ship is
126 * 13 3/01/99 10:00a Dave
127 * Fxied several dogfight related stats bugs.
129 * 12 2/26/99 6:01p Andsager
130 * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
132 * 11 1/26/99 10:09a Andsager
133 * Better checking for modifying/deleting variables
135 * 10 1/25/99 5:16p Andsager
136 * Handle change of variable type on modify-variable
138 * 9 1/25/99 8:10a Andsager
139 * Add sexp_modify_variable(). Changed syntax checking to allow, adding
140 * operator return type ambiguous
142 * 8 1/24/99 11:37p Dave
143 * First full rev of beam weapons. Very customizable. Removed some bogus
144 * Int3()'s in low level net code.
146 * 7 1/20/99 9:02a Andsager
147 * Fix bug in verify_and_fix_arguments, where list of strings can have
150 * 6 1/19/99 3:57p Andsager
151 * Round 2 of variables
153 * 5 12/17/98 2:39p Andsager
154 * Added bitmaps for campaign editor. Changed input into insert() to
157 * 4 12/17/98 2:34p Andsager
158 * new bitmap and dialog for campaign editor
160 * 3 10/13/98 9:27a Dave
161 * Started neatening up freespace.h
163 * 2 10/07/98 6:28p Dave
164 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
165 * Fred. Globalized mission and campaign file extensions. Removed Silent
166 * Threat specific code.
168 * 1 10/07/98 3:02p Dave
170 * 1 10/07/98 3:00p Dave
172 * 179 9/25/98 1:33p Andsager
173 * Add color to event editor (root and chain) indicating mission directive
175 * 178 9/15/98 4:26p Allender
176 * added sexpression help for some sexpressions
178 * 177 6/09/98 5:15p Lawrance
179 * French/German localization
181 * 176 5/21/98 12:58a Hoffoss
182 * Fixed warnings optimized build turned up.
184 * 175 5/15/98 6:45p Hoffoss
185 * Made some things not appear in the release version of Fred.
187 * 174 5/14/98 10:15a Allender
188 * add optional argument to prevous-goal/event operators to specify what
189 * sexpression should return when being played as a single mission
191 * 173 5/04/98 10:57a Johnson
192 * Fixed bug with labeled roots allowing insert.
194 * 172 4/25/98 7:39p Allender
195 * fixd some small hotkey stuff. Worked on turret orientation being
196 * correct for multiplayer. new sexpression called end-campaign will will
197 * end the main campaign
199 * 171 4/23/98 5:49p Hoffoss
200 * Added tracking of techroom database list info in pilot files, added
201 * sexp to add more to list, made mouse usable on ship listing in tech
204 * 170 4/15/98 3:46p Hoffoss
205 * Fixed bug with getting a default argument value from an opf listing was
206 * utilizing temporary memory that was being destroyed before we were
209 * 169 4/14/98 5:46p Hoffoss
210 * Added special-check operator.
212 * 168 4/14/98 5:24p Hoffoss
213 * Added a custom operator for training handling for Mike K.
215 * 167 4/14/98 4:19p Jim
216 * Fixed bug with deleting an argument to an operator that you shouldn't
219 * 166 4/07/98 10:51a Allender
220 * remove any allied from message senders. Make heads for mission
221 * specific messages play appropriately
223 * 165 4/03/98 12:17a Allender
224 * new sexpression to detect departed or destroyed. optionally disallow
225 * support ships. Allow docking with escape pods
227 * 164 3/30/98 2:57p Hoffoss
228 * Fixed event listing in campaign editor mode.
230 * 163 3/26/98 3:13p Duncan
231 * Fixed bug in goal name listing generation function. Allender forgot
232 * about an assumption being made with them when he used it for
235 * 162 3/23/98 2:46p Hoffoss
236 * Fixed bug with default argument available for OPF_MESSAGE even when
237 * there were no messages, and added "#Command" as a message source to
240 * 161 3/21/98 7:36p Lawrance
241 * Move jump nodes to own lib.
248 #include "sexp_tree.h"
251 #include "management.h"
253 #include "operatorargtypeselect.h"
254 #include "linklist.h"
255 #include "eventeditor.h"
256 #include "missiongoalsdlg.h"
258 #include "missionmessage.h"
259 #include "missioncampaign.h"
260 #include "campaigneditordlg.h"
261 #include "hudsquadmsg.h"
262 #include "ignoreordersdlg.h"
264 #include "controlsconfig.h"
265 #include "hudgauges.h"
266 #include "starfield.h"
267 #include "jumpnode.h"
268 #include "addvariabledlg.h"
269 #include "modifyvariabledlg.h"
271 #define MAX_OP_MENUS 30
273 #define ID_VARIABLE_MENU 0xda00
274 #define ID_ADD_MENU 0xdc00
275 #define ID_REPLACE_MENU 0xde00
276 // note: stay below 0xe000 so we don't collide with MFC defines..
279 #define new DEBUG_NEW
281 static char THIS_FILE[] = __FILE__;
284 BEGIN_MESSAGE_MAP(sexp_tree, CTreeCtrl)
285 //{{AFX_MSG_MAP(sexp_tree)
286 ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag)
291 ON_NOTIFY_REFLECT(TVN_KEYDOWN, OnKeydown)
295 static int Add_count, Replace_count;
296 static int Modify_variable;
298 struct sexp_help_struct {
303 { OP_PLUS, "Plus (Arithmetic operator)\r\n"
304 "\tAdds numbers and returns results.\r\n\r\n"
305 "Returns a number; Takes 2 or more numeric arguments." },
307 { OP_MINUS, "Minus (Arithmetic operator)\r\n"
308 "\tSubtracts numbers and returns results.\r\n\r\n"
309 "Returns a number; Takes 2 or more numeric arguments." },
311 { OP_MOD, "Mod (Arithmetic operator)\r\n"
312 "\tDivides numbers and returns the remainer.\r\n\r\n"
313 "Returns a number; Takes 2 or more numeric arguments." },
315 { OP_MUL, "Multiply (Arithmetic operator)\r\n"
316 "\tMultiplies numbers and returns results.\r\n\r\n"
317 "Returns a number; Takes 2 or more numeric arguments." },
319 { OP_DIV, "Divide (Arithmetic operator)\r\n"
320 "\tDivides numbers and returns results.\r\n\r\n"
321 "Returns a number; Takes 2 or more numeric arguments." },
323 { OP_RAND, "Random number (Arithmetic operator)\r\n"
324 "\tGets a random number and returns result.\r\n\r\n"
325 "Returns a number; Takes 2 numeric arguments.\r\n"
326 "\t1:\tLow range of random number.\r\n"
327 "\t2:\tHigh range of random number." },
329 { OP_TRUE, "True (Boolean operator)\r\n"
330 "\tA true boolean state\r\n\r\n"
331 "Returns a boolean value." },
333 { OP_FALSE, "False (Boolean operator)\r\n"
334 "\tA false boolean state\r\n\r\n"
335 "Returns a boolean value." },
337 { OP_AND, "And (Boolean operator)\r\n"
338 "\tAnd is true if all of it's arguments are true.\r\n\r\n"
339 "Returns a boolean value; Takes 2 or more boolean arguments." },
341 { OP_OR, "Or (Boolean operator)\r\n"
342 "\tOr is true if any of it's arguments are true.\r\n\r\n"
343 "Returns a boolean value; Takes 2 or more boolean arguments." },
345 { OP_EQUALS, "Equals (Boolean operator)\r\n"
346 "\tIs true if all of it's arguments are equal.\r\n\r\n"
347 "Returns a boolean value; Takes 2 or more numeric arguments." },
349 { OP_GREATER_THAN, "Greater than (Boolean operator)\r\n"
350 "\tTrue if first argument is greater than the second argument.\r\n\r\n"
351 "Returns a boolean value; Takes 2 numeric arguments." },
353 { OP_LESS_THAN, "Less than (Boolean operator)\r\n"
354 "\tTrue if first argument is less than the second argument.\r\n\r\n"
355 "Returns a boolean value; Takes 2 numeric arguments." },
357 { OP_IS_IFF, "Is IFF (Boolean operator)\r\n"
358 "\tTrue if ship{s} are all of the specified team.\r\n\r\n"
359 "Returns a boolean value; Takes 2 or more arguments:\r\n"
360 "/t1:\tTeam (\"friendly\", \"hostile\" or \"unknown\").\r\n"
361 "\tRest:\tName of ship to check." },
363 { OP_HAS_TIME_ELAPSED, "Has time elapsed (Boolean operator)\r\n"
364 "\tBecomes true when the specified amount of time has elapsed (Mission time "
365 "becomes greater than the specified time).\r\n"
366 "Returns a boolean value; Takes 1 numeric argument:\r\n"
367 "\t1:\tThe amount of time in seconds." },
369 { OP_NOT, "Not (Boolean operator)\r\n"
370 "\tReturns opposite boolean value of argument (True becomes false, and "
371 "false becomes true).\r\n\r\n"
372 "Returns a boolean value; Takes 1 boolean argument." },
374 { OP_PREVIOUS_GOAL_TRUE, "Previous Mission Goal True (Boolean operator)\r\n"
375 "\tReturns true if the specified goal in the specified mission is true "
376 "(or succeeded). It returns false otherwise.\r\n\r\n"
377 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
378 "\t1:\tName of the mission.\r\n"
379 "\t2:\tName of the goal in the mission.\r\n"
380 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
381 "this mission is played as a single mission." },
383 { OP_PREVIOUS_GOAL_FALSE, "Previous Mission Goal False (Boolean operator)\r\n"
384 "\tReturns true if the specified goal in the specified mission "
385 "is false (or failed). It returns false otherwise.\r\n\r\n"
386 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
387 "\t1:\tName of the mission.\r\n"
388 "\t2:\tName of the goal in the mission.\r\n"
389 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
390 "this mission is played as a single mission." },
392 { OP_PREVIOUS_GOAL_INCOMPLETE, "Previous Mission Goal Incomplete (Boolean operator)\r\n"
393 "\tReturns true if the specified goal in the specified mission "
394 "is incomplete (not true or false). It returns false otherwise.\r\n\r\n"
395 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
396 "\t1:\tName of the mission.\r\n"
397 "\t2:\tName of the goal in the mission.\r\n"
398 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
399 "this mission is played as a single mission." },
401 { OP_PREVIOUS_EVENT_TRUE, "Previous Mission Event True (Boolean operator)\r\n"
402 "\tReturns true if the specified event in the specified mission is true "
403 "(or succeeded). It returns false otherwise.\r\n\r\n"
404 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
405 "\t1:\tName of the mission.\r\n"
406 "\t2:\tName of the event in the mission.\r\n"
407 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
408 "this mission is played as a single mission." },
410 { OP_PREVIOUS_EVENT_FALSE, "Previous Mission Event False (Boolean operator)\r\n"
411 "\tReturns true if the specified event in the specified mission "
412 "is false (or failed). It returns false otherwise.\r\n\r\n"
413 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
414 "\t1:\tName of the mission.\r\n"
415 "\t2:\tName of the event in the mission.\r\n"
416 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
417 "this mission is played as a single mission." },
419 { OP_PREVIOUS_EVENT_INCOMPLETE, "Previous Mission Event Incomplete (Boolean operator)\r\n"
420 "\tReturns true if the specified event in the specified mission "
421 "is incomplete (not true or false). It returns false otherwise.\r\n\r\n"
422 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
423 "\t1:\tName of the mission.\r\n"
424 "\t2:\tName of the event in the mission.\r\n"
425 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
426 "this mission is played as a single mission." },
428 { OP_GOAL_TRUE_DELAY, "Mission Goal True (Boolean operator)\r\n"
429 "\tReturns true N seconds after the specified goal in the this mission is true "
430 "(or succeeded). It returns false otherwise.\r\n\r\n"
431 "Returns a boolean value; Takes 2 arguments:\r\n"
432 "\t1:\tName of the event in the mission.\r\n"
433 "\t2:\tNumber of seconds to delay before returning true."},
435 { OP_GOAL_FALSE_DELAY, "Mission Goal False (Boolean operator)\r\n"
436 "\tReturns true N seconds after the specified goal in the this mission is false "
437 "(or failed). It returns false otherwise.\r\n\r\n"
438 "Returns a boolean value; Takes 2 arguments:\r\n"
439 "\t1:\tName of the event in the mission.\r\n"
440 "\t2:\tNumber of seconds to delay before returning true."},
442 { OP_GOAL_INCOMPLETE, "Mission Goal Incomplete (Boolean operator)\r\n"
443 "\tReturns true if the specified goal in the this mission is incomplete. This "
444 "sexpression will only be useful in conjunction with another sexpression like"
445 "has-time-elapsed. Used alone, it will return true upon misison startup."
446 "Returns a boolean value; Takes 1 argument:\r\n"
447 "\t1:\tName of the event in the mission."},
449 { OP_EVENT_TRUE_DELAY, "Mission Event True (Boolean operator)\r\n"
450 "\tReturns true N seconds after the specified event in the this mission is true "
451 "(or succeeded). It returns false otherwise.\r\n\r\n"
452 "Returns a boolean value; Takes 2 arguments:\r\n"
453 "\t1:\tName of the event in the mission.\r\n"
454 "\t2:\tNumber of seconds to delay before returning true."},
456 { OP_EVENT_FALSE_DELAY, "Mission Event False (Boolean operator)\r\n"
457 "\tReturns true N seconds after the specified event in the this mission is false "
458 "(or failed). It returns false otherwise.\r\n\r\n"
459 "Returns a boolean value; Takes 2 arguments:\r\n"
460 "\t1:\tName of the event in the mission.\r\n"
461 "\t2:\tNumber of seconds to delay before returning true."},
463 { OP_EVENT_INCOMPLETE, "Mission Event Incomplete (Boolean operator)\r\n"
464 "\tReturns true if the specified event in the this mission is incomplete. This "
465 "sexpression will only be useful in conjunction with another sexpression like"
466 "has-time-elapsed. Used alone, it will return true upon misison startup."
467 "Returns a boolean value; Takes 1 argument:\r\n"
468 "\t1:\tName of the event in the mission."},
470 { OP_IS_DESTROYED_DELAY, "Is destroyed delay (Boolean operator)\r\n"
471 "\tBecomes true <delay> seconds after all specified ships have been destroyed.\r\n\r\n"
472 "Returns a boolean value; Takes 2 or more arguments:\r\n"
473 "\t1:\tTime delay in seconds (see above).\r\n"
474 "\tRest:\tName of ship (or wing) to check status of." },
476 { OP_IS_SUBSYSTEM_DESTROYED_DELAY, "Is subsystem destroyed delay (Boolean operator)\r\n"
477 "\tBecomes true <delay> seconds after the specified subsystem of the specified "
478 "ship is destroyed.\r\n\r\n"
479 "Returns a boolean value; Takes 3 arguments:\r\n"
480 "\t1:\tName of ship the subsystem we are checking is on.\r\n"
481 "\t2:\tThe name of the subsystem we are checking status of.\r\n"
482 "\t3:\tTime delay in seconds (see above)." },
484 { OP_IS_DISABLED_DELAY, "Is disabled delay (Boolean operator)\r\n"
485 "\tBecomes true <delay> seconds after the specified ship(s) are disabled. A "
486 "ship is disabled when all of it's engine subsystems are destroyed. All "
487 "ships must be diabled for this function to return true.\r\n\r\n"
488 "Returns a boolean value; Takes 2 or more arguments:\r\n"
489 "\t1:\tTime delay is seconds (see above).\r\n"
490 "\tRest:\tNames of ships to check disabled status of." },
492 { OP_IS_DISARMED_DELAY, "Is disarmed delay (Boolean operator)\r\n"
493 "\tBecomes true <delay> seconds after the specified ship(s) are disarmed. A "
494 "ship is disarmed when all of it's turret subsystems are destroyed. All "
495 "ships must be disarmed for this function to return true.\r\n\r\n"
496 "Returns a boolean value; Takes 2 or more arguments:\r\n"
497 "\t1:\tTime delay is seconds (see above).\r\n"
498 "\tRest:\tNames of ships to check disarmed status of." },
500 { OP_HAS_DOCKED_DELAY, "Has docked delay (Boolean operator)\r\n"
501 "\tBecomes true <delay> seconds after the specified ships have docked the "
502 "specified number of times.\r\n\r\n"
503 "Returns a boolean value; Takes 4 arguments:\r\n"
504 "\t1:\tThe name of the docker ship\r\n"
505 "\t2:\tThe name of the dockee ship\r\n"
506 "\t3:\tThe number of times they have to have docked\r\n"
507 "\t4:\tTime delay in seconds (see above)." },
509 { OP_HAS_UNDOCKED_DELAY, "Has undocked delay (Boolean operator)\r\n"
510 "\tBecomes true <delay> seconds after the specified ships have undocked the "
511 "specified number of times.\r\n\r\n"
512 "Returns a boolean value; Takes 4 arguments:\r\n"
513 "\t1:\tThe name of the docker ship\r\n"
514 "\t2:\tThe name of the dockee ship\r\n"
515 "\t3:\tThe number of times they have to have undocked\r\n"
516 "\t4:\tTime delay in seconds (see above)." },
518 { OP_HAS_ARRIVED_DELAY, "Has arrived delay (Boolean operator)\r\n"
519 "\tBecomes true <delay> seconds after the specified ship(s) have arrived into the mission\r\n\r\n"
520 "Returns a boolean value; Takes 2 or more arguments:\r\n"
521 "\t1:\tTime delay in seconds (see above).\r\n"
522 "\tRest:\tName of ship (or wing) we want to check has arrived." },
524 { OP_HAS_DEPARTED_DELAY, "Has departed delay (Boolean operator)\r\n"
525 "\tBecomes true <delay> seconds after the specified ship(s) or wing(s) have departed "
526 "from the mission by warping out. If any ship was destroyed, this operator will "
527 "never be true.\r\n\r\n"
528 "Returns a boolean value; Takes 2 or more arguments:\r\n"
529 "\t1:\tTime delay in seconds (see above).\r\n"
530 "\tRest:\tName of ship (or wing) we want to check has departed." },
532 { OP_WAYPOINTS_DONE_DELAY, "Waypoints done delay (Boolean operator)\r\n"
533 "\tBecomes true <delay> seconds after the specified ship has completed flying the "
534 "specified waypoint path.\r\n\r\n"
535 "Returns a boolean value; Takes 3 arguments:\r\n"
536 "\t1:\tName of ship we are checking.\r\n"
537 "\t2:\tWaypoint path we want to check if ship has flown.\r\n"
538 "\t3:\tTime delay in seconds (see above)." },
540 { OP_SHIP_TYPE_DESTROYED, "Ship Type Destroyed (Boolean operator)\r\n"
541 "\tBecomes true when the specified percentage of ship types in this mission "
542 "have been destroyed. The ship type is a generic type such as fighter/bomber, "
543 "transport, etc. Fighters and bombers count as the same type.\r\n\r\n"
544 "Returns a boolean value; Takes 2 arguments:\r\n"
545 "\t1:\tPercentage of ships that must be destroyed.\r\n"
546 "\t2:\tShip type to check for." },
548 { OP_TIME_SHIP_DESTROYED, "Time ship destroyed (Time operator)\r\n"
549 "\tReturns the time the specified ship was destroy.\r\n\r\n"
550 "Returns a numeric value; Takes 1 argument:\r\n"
551 "\t1:\tName of ship we want to check." },
553 { OP_TIME_SHIP_ARRIVED, "Time ship arrived (Time operator)\r\n"
554 "\tReturns the time the specified ship arrived into the mission.\r\n\r\n"
555 "Returns a numeric value; Takes 1 argument:\r\n"
556 "\t1:\tName of ship we want to check." },
558 { OP_TIME_SHIP_DEPARTED, "Time ship departed (Time operator)\r\n"
559 "\tReturns the time the specified ship departed the mission by warping out. Being "
560 "destroyed doesn't count departed.\r\n\r\n"
561 "Returns a numeric value; Takes 1 argument:\r\n"
562 "\t1:\tName of ship we want to check." },
564 { OP_TIME_WING_DESTROYED, "Time wing destroyed (Time operator)\r\n"
565 "\tReturns the time the specified wing was destroy.\r\n\r\n"
566 "Returns a numeric value; Takes 1 argument:\r\n"
567 "\t1:\tName of wing we want to check." },
569 { OP_TIME_WING_ARRIVED, "Time wing arrived (Time operator)\r\n"
570 "\tReturns the time the specified wing arrived into the mission.\r\n\r\n"
571 "Returns a numeric value; Takes 1 argument:\r\n"
572 "\t1:\tName of wing we want to check." },
574 { OP_TIME_WING_DEPARTED, "Time wing departed (Time operator)\r\n"
575 "\tReturns the time the specified wing departed the mission by warping out. All "
576 "ships in the wing have to have warped out. If any are destroyed, the wing can "
577 "never be considered departed.\r\n\r\n"
578 "Returns a numeric value; Takes 1 argument:\r\n"
579 "\t1:\tName of ship we want to check." },
581 { OP_MISSION_TIME, "Mission time (Time operator)\r\n"
582 "\tReturns the current time into the mission.\r\n\r\n"
583 "Returns a numeric value." },
585 { OP_TIME_DOCKED, "Time docked (Time operator)\r\n"
586 "\tReturns the time the specified ships docked.\r\n\r\n"
587 "Returns a numeric value; Takes 3 arguments:\r\n"
588 "\t1:\tThe name of the docker ship.\r\n"
589 "\t2:\tThe name of the dockee ship.\r\n"
590 "\t3:\tThe number of times they must have docked to be true." },
592 { OP_TIME_UNDOCKED, "Time undocked (Time operator)\r\n"
593 "\tReturns the time the specified ships undocked.\r\n\r\n"
594 "Returns a numeric value; Takes 3 arguments:\r\n"
595 "\t1:\tThe name of the docker ship.\r\n"
596 "\t2:\tThe name of the dockee ship.\r\n"
597 "\t3:\tThe number of times they must have undocked to be true." },
599 { OP_SHIELDS_LEFT, "Sheilds left (Status operator)\r\n"
600 "\tReturns the current level of the specified ship's shields as a percentage.\r\n\r\n"
601 "Returns a numeric value; Takes 1 argument:\r\n"
602 "\t1:\tName of ship to check." },
604 { OP_HITS_LEFT, "Hits left (Status operator)\r\n"
605 "\tReturns the current level of the specified ship's hull as a percentage.\r\n\r\n"
606 "Returns a numeric value; Takes 1 argument:\r\n"
607 "\t1:\tName of ship to check." },
609 { OP_HITS_LEFT_SUBSYSTEM, "Hits left subsystem (Status operator)\r\n"
610 "\tReturns the current level of the specified ship's subsystem integrity as a percentage.\r\n\r\n"
611 "Returns a numeric value; Takes 1 argument:\r\n"
612 "\t1:\tName of ship to check.\r\n"
613 "\t2:\tName of subsystem on ship to check." },
615 { OP_DISTANCE, "Distance (Misc. Operator)\r\n"
616 "\tReturns the distance between 2 objects. These objects can be either a ship, "
617 "a wing, or a waypoint.\r\n\r\n"
618 "Returns a numeric value; Takes 2 arguments:\r\n"
619 "\t1:\tThe name of one of the objects.\r\n"
620 "\t2:\tThe name of the other object." },
622 { OP_LAST_ORDER_TIME, "Last order time (Status operator)\r\n"
623 "\tReturns true if <count> seconds have elapsed since one or more ships have received "
624 "a meaningful order from the player. A meaningful order is currently any order that "
625 "is not the warp out order.\r\n\r\n"
626 "Returns a boolean value; Takes 2 or more arguments:\r\n"
627 "\t1:\tTime in seconds that must elapse.\r\n"
628 "\tRest:\tName of ship or wing to check for having received orders." },
630 { OP_WHEN, "When (Conditional operator)\r\n"
631 "\tPerforms specified actions when a condition becomes true\r\n\r\n"
632 "Takes 2 or more arguments:\r\n"
633 "\t1:\tBoolean expression that must be true for actions to take place.\r\n"
634 "\tRest:\tActions to take when boolean expression becomes true." },
638 { OP_CHANGE_IFF, "Change IFF (Action operator)\r\n"
639 "\tSets the specified ship(s) to the specified team.\r\n"
640 "Takes 2 or more arguments:\r\n"
641 "\t1:\tTeam to change to (\"friendly\", \"hostile\" or \"unknown\").\r\n"
642 "\tRest:\tName of ship to change team status of." },
644 { OP_MODIFY_VARIABLE, "Modify-variable (Misc. operator)\r\n"
645 "\tModifies variable to specified value\r\n\r\n"
646 "Takes 2 arguments:\r\n"
647 "\t1:\tName of Variable.\r\n"
648 "\t2:\tValue to be set." },
650 { OP_PROTECT_SHIP, "Protect ship (Action operator)\r\n"
651 "\tProtects a ship from being attacked by any enemy ship. Any ship"
652 "that is protected will not come under enemy fire.\r\n\r\n"
653 "Takes 1 or more arguments:\r\n"
654 "\tAll:\tName of ship(s) to protect." },
656 { OP_UNPROTECT_SHIP, "Unprotect ship (Action operator)\r\n"
657 "\tUnprotects a ship from being attacked by any enemy ship. Any ship"
658 "that is not protected can come under enemy fire. This function is the opposite"
659 "of protect-ship.\r\n\r\n"
660 "Takes 1 or more arguments:\r\n"
661 "\tAll:\tName of ship(s) to protect." },
663 { OP_BEAM_PROTECT_SHIP, "Beam Protect ship (Action operator)\r\n"
664 "\tProtects a ship from being attacked with beam weapon. Any ship"
665 "that is beam protected will not come under enemy beam fire.\r\n\r\n"
666 "Takes 1 or more arguments:\r\n"
667 "\tAll:\tName of ship(s) to protect." },
669 { OP_BEAM_UNPROTECT_SHIP, "Beam Unprotect ship (Action operator)\r\n"
670 "\tUnprotects a ship from being attacked with beam weapon. Any ship"
671 "that is not beam protected can come under enemy beam fire. This function is the opposite"
672 "of beam-protect-ship.\r\n\r\n"
673 "Takes 1 or more arguments:\r\n"
674 "\tAll:\tName of ship(s) to protect." },
676 { OP_SEND_MESSAGE, "Send message (Action operator)\r\n"
677 "\tSends a message to the player. Can be send by a ship, wing, or special "
678 "source. To send it from a special source, make the first character of the first "
679 "argument a \"#\".\r\n\r\n"
680 "Takes 3 arguments:\r\n"
681 "\t1:\tName of who the message is from.\r\n"
682 "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\").\r\n"
683 "\t3:\tName of message (from message editor)." },
685 { OP_SELF_DESTRUCT, "Self destruct (Action operator)\r\n"
686 "\tCauses the specified ship(s) to self destruct.\r\n\r\n"
687 "Takes 1 or more arguments:\r\n"
688 "\tAll:\tName of ship to self destruct." },
690 { OP_NEXT_MISSION, "Next Mission (Action operator)\r\n"
691 "\tThe next mission operator is used for campaign branching in the campaign editor. "
692 "It specifies which mission should played be next in the campaign. This operator "
693 "generally follows a 'when' or 'cond' statment in the campaign file.\r\n\r\n"
694 "Takes 1 argument:\r\n"
695 "\t1:\tName of mission (filename) to proceed to." },
697 { OP_CLEAR_GOALS, "Clear goals (Action operator)\r\n"
698 "\tClears the goals for the specified ships and/or wings.\r\n\r\n"
699 "Takes 1 or more arguments:\r\n"
700 "\tAll:\tName of ship or wing." },
702 { OP_ADD_GOAL, "Add goal (Action operator)\r\n"
703 "\tAdds a goal to a ship or wing.\r\n\r\n"
704 "Takes 2 arguments:\r\n"
705 "\t1:\tName of ship or wing to all goal to.\r\n"
706 "\t2:\tGoal to add." },
708 { OP_SABOTAGE_SUBSYSTEM, "Sabotage subystem (Action operator)\r\n"
709 "\tReduces the specified subsystem integrity by the specified percentage."
710 "If the percntage strength of the subsystem (after completion) is less than 0%,"
711 "subsystem strength is set to 0%.\r\n\r\n"
712 "Takes 3 arguments:\r\n"
713 "\t1:\tName of ship subsystem is on.\r\n"
714 "\t2:\tName of subsystem to sabotage.\r\n"
715 "\t3:\tPercentage to reduce subsystem integrity by." },
717 { OP_REPAIR_SUBSYSTEM, "Repair Subystem (Action operator)\r\n"
718 "\tIncreases the specified subsystem integrity by the specified percentage."
719 "If the percntage strength of the subsystem (after completion) is greater than 100%,"
720 "subsystem strength is set to 100%.\r\n\r\n"
721 "Takes 3 arguments:\r\n"
722 "\t1:\tName of ship subsystem is on.\r\n"
723 "\t2:\tName of subsystem to repair.\r\n"
724 "\t3:\tPercentage to increase subsystem integrity by." },
726 { OP_SET_SUBSYSTEM_STRNGTH, "Set Subsystem Strength (Action operator)\r\n"
727 "\tSets the specified subsystem to the the specified percentage."
728 "If the percentage specified is < 0, strength is set to 0. If the percentage is "
729 "> 100 % the subsystem strength is set to 100%.\r\n\r\n"
730 "Takes 3 arguments:\r\n"
731 "\t1:\tName of ship subsystem is on.\r\n"
732 "\t2:\tName of subsystem to set strength.\r\n"
733 "\t3:\tPercentage to set subsystem integrity to." },
735 { OP_INVALIDATE_GOAL, "Invalidate goal (Action operator)\r\n"
736 "\tMakes a mission goal invalid, which causes it to now show up on mission goals "
737 "screen, or be evaluated.\r\n"
738 "Takes 1 or more arguments:\r\n"
739 "\tAll:\tName of mission goal to invalidate." },
741 { OP_VALIDATE_GOAL, "Validate goal (Action operator)\r\n"
742 "\tMakes a mission goal valid again, so it shows up on mission goals screen.\r\n"
743 "Takes 1 or more arguments:\r\n"
744 "\tAll:\tName of mission goal to validate." },
746 { OP_SEND_RANDOM_MESSAGE, "Send random message (Action operator)\r\n"
747 "\tSends a random message to the player from those supplied. Can be send by a "
748 "ship, wing, or special source. To send it from a special source, make the first "
749 "character of the first argument a \"#\".\r\n\r\n"
750 "Takes 3 or more arguments:\r\n"
751 "\t1:\tName of who the message is from.\r\n"
752 "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\")."
753 "\tRest:\tName of message (from message editor)." },
755 { OP_TRANSFER_CARGO, "Transfer Cargo (Action operator)\r\n"
756 "\tTransfers the cargo from one ship to another ship.\r\n\r\n"
757 "Takes 2 arguments:\r\n"
758 "\t1:\tName of ship that cargo is being transferred from.\r\n"
759 "\t2:\tName of ship that cargo is being transferred to." },
761 { OP_EXCHANGE_CARGO, "Exchange Cargo (Action operator)"
762 "\tExchanges the cargos of two ships. If one of the two ships contains no cargo, "
763 "the cargo is transferred instead.\r\n"
764 "Takes 2 arguments:\r\n"
765 "\t1:\tName of one of the ships.\r\n"
766 "\t2:\tName of the other ship." },
768 { OP_INT3, "Error (Debug directive)\r\n"
769 "Causes the game to halt with an error." },
771 { OP_AI_CHASE, "Ai-chase (Ship goal)\r\n"
772 "\tCauses the specified ship to chase and attack the specified target.\r\n\r\n"
773 "Takes 2 arguments:\r\n"
774 "\t1:\tName of ship to chase.\r\n"
775 "\t2:\tGoal priority (number between 0 and 89)." },
777 { OP_AI_DOCK, "Ai-dock (Ship goal)\r\n"
778 "\tCauses one ship to dock with another ship.\r\n\r\n"
779 "Takes 4 arguments:\r\n"
780 "\t1:\tName of dockee ship (The ship that \"docker\" will dock with).\r\n"
781 "\t2:\tDocker's docking point - Which dock point docker uses to dock.\r\n"
782 "\t3:\tDockee's docking point - Which dock point on dockee docker will move to.\r\n"
783 "\t4:\tGoal priority (number between 0 and 89)." },
785 { OP_AI_UNDOCK, "Ai-undock (Ship goal)\r\n"
786 "\tCauses the specified ship to undock from who it is currently docked with.\r\n\r\n"
787 "Takes 1 arguments:\r\n"
788 "\t1:\tGoal priority (number between 0 and 89)." },
790 { OP_AI_WARP_OUT, "Ai-warp-out (Ship/Wing Goal)\r\n"
791 "\tCauses the specified ship/wing to warp out of the mission. Currently, the ship will "
792 "warp out at it's current location. This behavior will change. Currently, the first "
793 "argument means nothing.\r\n\r\n"
794 "Takes 2 arguments:\r\n"
795 "\t1:\tName of waypoint path to follow to warp out (not used).\r\n"
796 "\t2:\tGoal priority (number between 0 and 89)." },
798 { OP_AI_WAYPOINTS, "Ai-waypoints (Ship goal)\r\n"
799 "\tCauses the specified ship to fly a waypoint path continuously.\r\n\r\n"
800 "Takes 2 arguments:\r\n"
801 "\t1:\tName of waypoint path to fly.\r\n"
802 "\t2:\tGoal priority (number between 0 and 89)." },
804 { OP_AI_WAYPOINTS_ONCE, "Ai-waypoints once (Ship goal)\r\n"
805 "\tCauses the specified ship to fly a waypoint path.\r\n\r\n"
806 "Takes 2 arguments:\r\n"
807 "\t1:\tName of waypoint path to fly.\r\n"
808 "\t2:\tGoal priority (number between 0 and 89)." },
810 { OP_AI_DESTROY_SUBSYS, "Ai-destroy subsys (Ship goal)\r\n"
811 "\tCauses the specified ship to attack and try and destroy the specified subsystem "
812 "on the specified ship.\r\n\r\n"
813 "Takes 3 arguments:\r\n"
814 "\t1:\tName of ship subsystem is on.\r\n"
815 "\t2:\tName of subsystem on the ship to attack and destroy.\r\n"
816 "\t3:\tGoal priority (number between 0 and 89)." },
818 { OP_AI_CHASE_WING, "Ai-chase wing (Ship goal)\r\n"
819 "\tCauses the specified ship to chase and attack the specified target.\r\n\r\n"
820 "Takes 2 arguments:\r\n"
821 "\t1:\tName of wing to chase.\r\n"
822 "\t2:\tGoal priority (number between 0 and 89)." },
824 { OP_AI_DISABLE_SHIP, "Ai-disable-ship (Ship/wing goal)\r\n"
825 "\tThis AI goal causes a ship/wing to destroy all of the engine subsystems on "
826 "the specified ship. This goal is different than ai-destroy-subsystem since a ship "
827 "may have multiple engine subsystems requiring the use of > 1 ai-destroy-subsystem "
829 "Takes 2 arguments:\r\n"
830 "\t1:\tName of ship whose engine subsystems should be destroyed\r\n"
831 "\t2:\tGoal priority (number between 0 and 89)." },
833 { OP_AI_DISARM_SHIP, "Ai-disarm-ship (Ship/wing goal)\r\n"
834 "\tThis AI goal causes a ship/wing to destroy all of the turret subsystems on "
835 "the specified ship. This goal is different than ai-destroy-subsystem since a ship "
836 "may have multiple turret subsystems requiring the use of > 1 ai-destroy-subsystem "
838 "Takes 2 arguments:\r\n"
839 "\t1:\tName of ship whose turret subsystems should be destroyed\r\n"
840 "\t2:\tGoal priority (number between 0 and 89)." },
842 { OP_AI_GUARD, "Ai-guard (Ship goal)\r\n"
843 "\tCauses the specified ship to guard a ship from other ships not on the same team.\r\n\r\n"
844 "Takes 2 arguments:\r\n"
845 "\t1:\tName of ship to guard.\r\n"
846 "\t2:\tGoal priority (number between 0 and 89)." },
848 { OP_AI_CHASE_ANY, "Ai-chase any (Ship goal)\r\n"
849 "\tCauses the specified ship to chase and attack any ship on the opposite team.\r\n\r\n"
850 "Takes 1 arguments:\r\n"
851 "\t1:\tGoal priority (number between 0 and 89)." },
853 { OP_AI_GUARD_WING, "Ai-guard wing (Ship goal)\r\n"
854 "\tCauses the specified ship to guard a wing of ships from other ships not on the "
856 "Takes 2 arguments:\r\n"
857 "\t1:\tName of wing to guard.\r\n"
858 "\t2:\tGoal priority (number between 0 and 89)." },
860 { OP_NOP, "Do-nothing (Action operator)\r\n"
861 "\tDoes nothing. This is used as the default for any required action arguments "
864 { OP_KEY_PRESSED, "Key-pressed (Boolean training operator)\r\n"
865 "\tBecomes true when the specified default key has been pressed. Default key "
866 "refers to the what the key normally is when not remapped. FreeSpace will "
867 "automatically account for any keys that have been remapped. If the optional "
868 "delay is specified, becomes true that many seconds after the key has been pressed.\r\n\r\n"
869 "Returns a boolean value; Takes 1 or 2 arguments:\r\n"
870 "\t1:\tDefault key to check for.\r\n"
871 "\t2:\tDelay before operator registers as true (optional).\r\n" },
873 { OP_KEY_RESET, "Key-reset (Training operator)\r\n"
874 "\tMarks the specified default key as having not been pressed, so key-pressed will be false "
875 "until the player presses it again. See key-pressed help for more information about "
876 "what a default key is.\r\n\r\n"
877 "Returns a boolean value; Takes 1 argument:\r\n"
878 "\t1:\tDefault key to reset." },
880 { OP_TARGETED, "Targeted (Boolean training operator)\r\n"
881 "\tIs true as long as the player has the specified ship (or ship's subsystem) targeted, "
882 "or has been targeted for the specified amount of time.\r\n\r\n"
883 "Returns a boolean value; Takes 1 to 3 arguments (first required, rest optional):\r\n"
884 "\t1:\tName of ship to check if targeted by player.\r\n"
885 "\t2:\tLength of time target should have been kept for (optional).\r\n"
886 "\t3:\tName of subsystem on ship to check if targeted (optional)." },
888 { OP_SPEED, "Speed (Boolean training operator)\r\n"
889 "\tBecomes true when the player has been within the specified speed range set by "
890 "set-training-context-speed for the specified amount of time.\r\n\r\n"
891 "Returns a boolean value; Takes 1 argument:\r\n"
892 "\t1:\tTime in seconds." },
894 { OP_FACING, "Facing (Boolean training operator)\r\n"
895 "\tIs true as long as the specified ship is within the player's specified "
896 "forward cone. A forward cone is defined as any point that the angle between the "
897 "vector of the point and the player, and the forward facing vector is within the "
898 "given angle.\r\n\r\n"
899 "Returns a boolean value; Takes 2 argument:\r\n"
900 "\t1:\tShip to check is withing forward cone.\r\n"
901 "\t2:\tAngle in degrees of the forward cone." },
903 { OP_FACING2, "Facing Waypoint(Boolean training operator)\r\n"
904 "\tIs true as long as the specified first waypoint is within the player's specified "
905 "forward cone. A forward cone is defined as any point that the angle between the "
906 "vector of the point and the player, and the forward facing vector is within the "
907 "given angle.\r\n\r\n"
908 "Returns a boolean value; Takes 2 argument:\r\n"
909 "\t1:\tName of waypoint path whose first point is withing forward cone.\r\n"
910 "\t2:\tAngle in degrees of the forward cone." },
912 { OP_ORDER, "Order (Boolean training operator)\r\n"
913 "\tBecomes true when the player had given the specified ship or wing the specified order.\r\n\r\n"
914 "Returns a boolean value; Takes 2 arguments:\r\n"
915 "\t1:\tName of ship or wing to check if given order to.\r\n"
916 "\t2:\tName of order to check if player has given." },
918 { OP_WAYPOINT_MISSED, "Waypoint-missed (Boolean training operator)\r\n"
919 "\tBecomes true when a waypoint is flown, but the waypoint is ahead of the one "
920 "they are supposed to be flying. The one they are supposed to be flying is the "
921 "next one in sequence in the path after the last one they have hit.\r\n\r\n"
922 "Returns a boolean value; Takes no arguments." },
924 { OP_PATH_FLOWN, "Path-flown (Boolean training operator)\r\n"
925 "\tBecomes true when all the waypoints in the path have been flown, in sequence.\r\n\r\n"
926 "Returns a boolean value; Takes no arguments." },
928 { OP_WAYPOINT_TWICE, "Waypoint-twice (Boolean training operator)\r\n"
929 "\tBecomes true when a waypoint is hit that is before the last one hit, which "
930 "indicates they have flown a waypoint twice.\r\n\r\n"
931 "Returns a boolean value; Takes no arguments." },
933 { OP_TRAINING_MSG, "Training-msg (Action training operator)\r\n"
934 "\tSends the player a training message. Uses the same messages as normal messages, "
935 "only they get displayed differently using this operator. If a secondary message "
936 "is specified, it is sent the last time, while the primary message is sent all other "
937 "times (event should have a repeat count greater than 1).\r\n\r\n"
938 "Takes 1-3 arguments:\r\n"
939 "\t1:\tName of primary message to send.\r\n"
940 "\t2:\tName of secondary message to send (optional).\r\n"
941 "\t3:\tDelay (in seconds) to wait before sending message. (optional)\r\n"
942 "\t4:\tAmount of Time (in seconds) to display message (optional)." },
944 { OP_SET_TRAINING_CONTEXT_FLY_PATH, "Set-training-context-fly-path (Training context operator)\r\n"
945 "\tTells FreeSpace that the player is expected to fly a waypoint path. This must be "
946 "executed before waypoint-missed, waypoint-twice and path-flown operators become valid.\r\n\r\n"
947 "Takes 2 arguments:\r\n"
948 "\t1:\tName of waypoint path player should fly.\r\n"
949 "\t2:\tDistance away a player needs to be from a waypoint for it to be registered as flown." },
951 { OP_SET_TRAINING_CONTEXT_SPEED, "Set-training-context-speed (Training context operator)\r\n"
952 "\tTells FreeSpace that the player is expected to fly within a certain speed range. Once "
953 "this operator has been executed, you can measure how long they have been within this "
954 "speed range with the speed operator.\r\n\r\n"
955 "Takes 2 arguments:\r\n"
956 "\t1:\tMinimum speed of range player is to fly between.\r\n"
957 "\t2:\tMaximum speed of range player is to fly between." },
959 { OP_GRANT_PROMOTION, "Grant promotion (Action operator)\r\n"
960 "\tIn a single player game, this function grants a player an automatic promotion to the "
961 "next rank which the player can obtain. If he is already at the highest rank, this "
962 "operator has no effect. It takes no arguments." },
964 { OP_GRANT_MEDAL, "Grant medal (Action operator)\r\n"
965 "\tIn single player missions, this function grants the given medal to the player. "
966 "Currently, only 1 medal will be allowed to be given per mission.\r\n\r\n"
967 "Takes 1 argument:\r\n"
968 "\t1:\tName of medal to grant to player." },
970 { OP_GOOD_SECONDARY_TIME, "Set prefered secondary weapons\r\n"
971 "\tThis sexpression is used to inform the AI about prefered secondary weapons to "
972 "fire during combat. When this expression is evaulated, any AI ships of the given "
973 "team prefer to fire the given weapon at the given ship. (Prefered over other "
974 "secondary weapons)\r\n\r\n"
975 "Takes 4 argument:\r\n"
976 "\t1:\tTeam name which will prefer firing given weapon\r\n"
977 "\t2:\tMaximum number of this type of weapon above team can fire.\r\n"
978 "\t3:\tWeapon name (list includes only the valid weapons for this expression\r\n"
979 "\t4:\tShip name at which the above named team should fire the above named weapon." },
981 { OP_AND_IN_SEQUENCE, "And in sequence (Boolean operator)\r\n"
982 "\tReturns true if all of it's arguments have become true in the order they are "
984 "Returns a boolean value; Takes 2 or more boolean arguments." },
986 { OP_SKILL_LEVEL_AT_LEAST, "Skill level at least (Boolean operator)\r\n"
987 "\tReturns true if the player has selected the given skill level or higher.\r\n\r\n"
988 "Returns a boolean value; Takes 1 arguments:\r\n"
989 "\t1:\tName of the skill level to check." },
991 { OP_NUM_PLAYERS, "Num players (Status operator)\r\n"
992 "\tReturns the current number of players (multiplayer) playing in the current mission.\r\n\r\n"
993 "Returns a numeric value; Takes no arguments." },
995 { OP_IS_CARGO_KNOWN, "Is cargo known (Boolean operator)\r\n"
996 "\tReturns true if all of the specified objects' cargo is known by the player (i.e. they "
997 "have scanned each one.\r\n\r\n"
998 "Returns a boolean value; Takes 1 or more arguments:\r\n"
999 "\tAll:\tName of ship to check if it's cargo is known." },
1001 { OP_HAS_BEEN_TAGGED_DELAY, "Has ship been tagged (delay) (Boolean operator)\r\n"
1002 "\tReturns true if all of the specified ships have been tagged.\r\n\r\n"
1003 "Returns a boolean value after <delay> seconds when all ships have been tagged; Takes 2 or more arguments:\r\n"
1004 "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned."
1005 "\tRest:\tNames of ships to check if tagged.." },
1007 { OP_CAP_SUBSYS_CARGO_KNOWN_DELAY, "Is capital ship subsystem cargo known (delay) (Boolean operator)\r\n"
1008 "\tReturns true if all of the specified subsystem cargo is known by the player.\r\n"
1009 "\tNote: Cargo must be explicitly named.\r\n\r\n"
1010 "Returns a boolean value after <delay> seconds when all cargo is known; Takes 3 or more arguments:\r\n"
1011 "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned.\r\n"
1012 "\t2:\tName of Captial ship\r\n"
1013 "\tRest:\tNames of subsystems to check for cargo known.." },
1015 { OP_CARGO_KNOWN_DELAY, "Is cargo known (delay) (Boolean operator)\r\n"
1016 "\tReturns true if all of the specified objects' cargo is known by the player (i.e. they "
1017 "have scanned each one.\r\n\r\n"
1018 "Returns a boolean value after <delay> seconds when all cargo is known; Takes 2 or more arguments:\r\n"
1019 "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned."
1020 "\tRest:\tNames of ships/cargo to check for cargo known.." },
1022 { OP_WAS_PROMOTION_GRANTED, "Was promotion granted (Boolean operator)\r\n"
1023 "\tReturns true if a promotion was granted via the 'Grant promotion' operator in the mission.\r\n\r\n"
1024 "Returns a boolean value; Takes no arguments." },
1026 { OP_WAS_MEDAL_GRANTED, "Was medal granted (Boolean operator)\r\n"
1027 "\tReturns true if a medal was granted via via the 'Grant medal' operator in the mission. "
1028 "If you provide the optional argument to this operator, then true is only returned if the "
1029 "specified medal was granted.\r\n\r\n"
1030 "Returns a boolean value; Takes 0 or 1 arguments:\r\n"
1031 "\t1:\tName of medal to specifically check for (optional)." },
1033 { OP_GOOD_REARM_TIME, "Good rearm time (Action operator)\r\n"
1034 "\tInforms the game logic that right now is a good time for a given team to attempt to "
1035 "rearm their ships. The time parameter specified how long the \"good time\" will last.\r\n\r\n"
1036 "Takes 2 arguments:\r\n"
1037 "\t1:\tTeam Name\r\n"
1038 "\t2:\tTime in seconds rearm window should last" },
1040 { OP_ALLOW_SHIP, "Allow ship (Action operator)\r\n"
1041 "\tThis operator makes the given ship type available to the Terran team. Players will be "
1042 "able to have ships of this type in their starting wings in all future missions of this "
1044 "Takes 1 arguments:\r\n"
1045 "\t1:\tName of ship type (or ship class) to allow." },
1047 { OP_ALLOW_WEAPON, "Allow weapon (Action operator)\r\n"
1048 "\tThis operator makes the given weapon available to the Terran team. Players will be "
1049 "able to equip ships with in all future missions of this campaign.\r\n\r\n"
1050 "Takes 1 arguments:\r\n"
1051 "\t1:\tName of weapon (primary or secondary) to allow." },
1053 { OP_TECH_ADD_SHIP, "Tech add ship (Action operator)\r\n"
1054 "\tThis operator makes the given ship type available in the techroom database. Players will "
1055 "then be able to view this ship's specs there.\r\n\r\n"
1056 "Takes 1 or more arguments:\r\n"
1057 "\tAll:\tName of ship type (or ship class) to add." },
1059 { OP_TECH_ADD_WEAPON, "Tech add weapon (Action operator)\r\n"
1060 "\tThis operator makes the given weapon available in the techroom database. Players will "
1061 "then be able to view this weapon's specs there.\r\n\r\n"
1062 "Takes 1 or more arguments:\r\n"
1063 "\tAll:\tName of weapon (primary or secondary) to add." },
1065 { OP_AI_EVADE_SHIP, "Ai-evade ship (Ship goal)\r\n"
1066 "\tCauses the specified ship to go into evade mode and run away like the weak "
1067 "sally-boy it is.\r\n\r\n"
1068 "Takes 2 arguments:\r\n"
1069 "\t1:\tName of ship to evade from.\r\n"
1070 "\t2:\tGoal priority (number between 0 and 89)." },
1072 { OP_AI_STAY_NEAR_SHIP, "Ai-stay near ship (Ship goal)\r\n"
1073 "\tCauses the specified ship to keep itself near the given ship and not stray too far "
1074 "away from it.\r\n\r\n"
1075 "Takes 2 arguments:\r\n"
1076 "\t1:\tName of ship to stay near.\r\n"
1077 "\t2:\tGoal priority (number between 0 and 89)." },
1079 { OP_AI_KEEP_SAFE_DISTANCE, "Ai-keep safe distance (Ship goal)\r\n"
1080 "\tTells the specified ship to stay a safe distance away from any ship that isn't on the "
1081 "same team as it.\r\n\r\n"
1082 "Takes 1 argument:\r\n"
1083 "\t1:\tGoal priority (number between 0 and 89)." },
1085 { OP_AI_IGNORE, "Ai-ignore (Ship goal)\r\n"
1086 "\tTells the specified ship to ignore the given ship and not consider it as a valid "
1087 "target to attack.\r\n\r\n"
1088 "Takes 2 arguments:\r\n"
1089 "\t1:\tName of ship to ignore.\r\n"
1090 "\t2:\tGoal priority (number between 0 and 89)." },
1092 { OP_AI_STAY_STILL, "Ai-stay still (Ship goal)\r\n"
1093 "\tCauses the specified ship to stay still. The ship will do nothing until attacked at "
1094 "which time the ship will come to life and defend itself.\r\n\r\n"
1095 "Takes 2 arguments:\r\n"
1096 "\t1:\tShip or waypoint the ship staying still will directly face (currently not implemented)\r\n"
1097 "\t2:\tGoal priority (number between 0 and 89)." },
1099 { OP_AI_PLAY_DEAD, "Ai-play dead (Ship goal)\r\n"
1100 "\tCauses the specified ship to pretend that it is dead and not do anything. This "
1101 "expression should be used to indicate that a ship has no pilot and cannot respond "
1102 "to any enemy threats. A ship playing dead will not respond to any attack. This "
1103 "should really be named ai-is-dead\r\n\r\n"
1104 "Takes 1 argument:\r\n"
1105 "\t1:\tGoal priority (number between 0 and 89)." },
1107 { OP_FLASH_HUD_GAUGE, "Ai-flash hud gauge (Training goal)\r\n"
1108 "\tCauses the specified hud gauge to flash to draw the player's attention to it.\r\n\r\n"
1109 "Takes 1 argument:\r\n"
1110 "\t1:\tName of hud gauge to flash." },
1112 { OP_SHIP_VISIBLE, "ship-visible\r\n"
1113 "\tCauses the ships listed in this sexpression to be visible with player sensors.\r\n\r\n"
1114 "Takes 1 or more arguments:\r\n"
1115 "\t1+:\tName of ships to make visible to sensors." },
1117 { OP_SHIP_INVISIBLE, "ship-invisible\r\n"
1118 "\tCauses the ships listed in this sexpression to be invisible to player sensors.\r\n\r\n"
1119 "Takes 1 or more arguments:\r\n"
1120 "\t1+:\tName of ships to make invisible to sensors." },
1122 { OP_SHIP_VULNERABLE, "ship-vulnerable\r\n"
1123 "\tCauses the ship listed in this sexpression to be vulnerable to weapons.\r\n\r\n"
1124 "Takes 1 or more arguments:\r\n"
1125 "\t1+:\tName of ships to make vulnerable to weapons." },
1127 { OP_SHIP_INVULNERABLE, "ship-invulnerable\r\n"
1128 "\tCauses the ships listed in this sexpression to be invulnerable to weapons. Use with caution!!!!\r\n\r\n"
1129 "Takes 1 or more arguments:\r\n"
1130 "\t1+:\tName of ships to make invulnerable to weapons." },
1132 { OP_SHIP_GUARDIAN, "ship-guardian\r\n"
1133 "\tCauses the ships listed in this sexpression to not be killable by weapons. Use with caution!!!!\r\n\r\n"
1134 "Takes 1 or more arguments:\r\n"
1135 "\t1+:\tName of ships to make invulnerable to weapons." },
1137 { OP_SHIP_NO_GUARDIAN, "ship-no-guardian\r\n"
1138 "\tCauses the ships listed in this sexpression to be killable by weapons, if not invulnerable.\r\n\r\n"
1139 "Takes 1 or more arguments:\r\n"
1140 "\t1+:\tName of ships to make vulnerable to weapons." },
1142 { OP_PERCENT_SHIPS_DEPARTED, "percent-ships-departed\r\n"
1143 "\tBoolean function which returns true if the percentage of ships in the listed ships and wings "
1144 "which have departed is greater or equal to the given percentage. For wings, all ships of all waves "
1145 "are used for calculation for the total possible ships to depart.\r\n\r\n"
1146 "Takes 2 or more arguments:\r\n"
1147 "\t1:\tPercentge of departed ships at which this function will return true.\r\n"
1148 "\t2+:\tList of ships/wing whose departure status should be determined." },
1150 { OP_PERCENT_SHIPS_DESTROYED, "percent-ships-destroyed\r\n"
1151 "\tBoolean function which returns true if the percentage of ships in the listed ships and wings "
1152 "which have been destroyed is greater or equal to the given percentage. For wings, all ships of all waves "
1153 "are used for calculation for the total possible ships to be destroyed.\r\n\r\n"
1154 "Takes 2 or more arguments:\r\n"
1155 "\t1:\tPercentge of destroyed ships at which this function will return true.\r\n"
1156 "\t2+:\tList of ships/wing whose destroyed status should be determined." },
1158 { OP_RED_ALERT, "red-alert\r\n"
1159 "\tCauses Red Alert status in a mission. This function ends the current mission, and moves to "
1160 "the next mission in the campaign under red alert status. There should only be one branch from "
1161 "a mission that uses this expression\r\n\r\n"
1162 "Takes no arguments."},
1164 { OP_DEPART_NODE_DELAY, "depart-node-delay\r\n"
1165 "\tReturns true N seconds after the listed ships depart, if those ships depart within the "
1166 "radius of the given jump node. The delay value is given in seconds.\r\n\r\n"
1167 "Takes 3 or more arguments:r\n"
1168 "\t1:\tDelay in seconds after the last ship listed departe before this expression can return true.\r\n"
1169 "\t2:\tName of a jump node\r\n"
1170 "\t3+:\tList of ships to check for departure within radius of the jump node." },
1172 { OP_DESTROYED_DEPARTED_DELAY, "destroyed-or-departed-delay\r\n"
1173 "\tReturns true N seconds after all the listed ships or wings have been destroyed or have "
1175 "Takes 2 or more arguments:\r\n"
1176 "\t1:\tDelay in seconda after the last ship/wing is destroyed or departerd this expression can return true.\r\n"
1177 "\t2+:\tName of a ship or wing" },
1179 { OP_SPECIAL_CHECK, "Special-check\r\n"
1180 "\tDo some special check in training. Ask Mike K. about how it works.\r\n\r\n"
1181 "Returns a boolean value; Takes 1 argument:\r\n"
1182 "\t1:\tExtra special number (tm)" },
1184 { OP_END_CAMPAIGN, "end-campaign\r\n"
1185 "\tEnds the builtin campaign. Should only be used by the main FreeSpace campaign\r\n" },
1187 { OP_WARP_BROKEN, "break-warp\r\n"
1188 "\tBreak the warp drive on the specified ship. A broken warp drive can be repaired by "
1189 "a repair ship. Takes 1 or more arguments:\r\n"
1190 "\t1:\tList of ships to break the warp drive on" },
1191 { OP_WARP_NOT_BROKEN, "fix-warp\r\n"
1192 "\tFixes a broken warp drive instantaneously. This option applies to warp drives broken with "
1193 "the break-warp sepxression. Takes 1 or more arguments:\r\n"
1194 "\t1:\tList of ships whose warp drive should be fixed"},
1195 { OP_WARP_NEVER, "never-warp\r\n"
1196 "\tNever allows a ship to warp out. When this sexpression is used, the given ships will "
1197 "never be able to warp out. The warp drive cannot be repaired. Takes 1 or more arguments:\r\n"
1198 "\t1:\tList of ships whose are not allowed to warp out under any condition"},
1199 { OP_WARP_ALLOWED, "allow-warp\r\n"
1200 "\tAllows a ship which was previously not allowed to warp out to do so. When this sexpression is "
1201 "used, the given ships will be able to warp out again. Takes 1 or more arguments:\r\n"
1202 "\t1:\tList of ships whose are allowed to warp out"},
1203 { OP_JETTISON_CARGO, "jettison-cargo-delay\r\n"
1204 "\tCauses a cargo carrying ship to jettison its cargo without the undocking procedure. Takes 2 arguments"},
1205 { OP_BEAM_FIRE, "beam-fire\r\n"
1206 "\tFire a beam weapon from a specified subsystem\r\n"
1207 "\t1:\tShip which will be firing\r\n"
1208 "\t2:\tTurret which will fire the beam (note, this turret must have at least 1 beam weapon on it)\r\n"
1209 "\t3:\tShip which will be targeted\r\n"
1210 "Use add-data to add a specific subsystem to target on the specified target ship"},
1211 { OP_IS_TAGGED, "is-tagged\r\n"
1212 "\tReturns whether a given ship is tagged or not\r\n"},
1213 { OP_NUM_KILLS, "num-kills\r\n"
1214 "\tReturns the # of kills a player has. The ship specified in the first field should be the ship the player is in.\r\n"
1215 "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
1216 "\ttime there is no player in a given ship, this sexpression will return 0"},
1217 { OP_NUM_TYPE_KILLS, "num-type-kills\r\n"
1218 "\tReturns the # of kills a player has on a given ship type (fighter, bomber, cruiser, etc).\r\n"
1219 "The ship specified in the first field should be the ship the player is in.\r\n"
1220 "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
1221 "\ttime there is no player in a given ship, this sexpression will return 0"},
1222 { OP_NUM_CLASS_KILLS, "num-class-kills\r\n"
1223 "\tReturns the # of kills a player has on a specific ship class (Ulysses, Hercules, etc).\r\n"
1224 "The ship specified in the first field should be the ship the player is in.\r\n"
1225 "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
1226 "\ttime there is no player in a given ship, this sexpression will return 0"},
1227 { OP_BEAM_FREE, "beam-free\r\n"
1228 "\tSets one or more beam weapons to allow firing for a given ship\r\n"
1229 "\t1: Ship to be operated on\r\n"
1230 "\t2, 3, etc : List of turrets to activate\r\n"},
1231 { OP_BEAM_FREE_ALL, "beam-free-all\r\n"
1232 "\tSets all beam weapons on the specified ship to be active\r\n"},
1233 { OP_BEAM_LOCK, "beam-lock\r\n"
1234 "\tSets one or more beam weapons to NOT allow firing for a given ship\r\n"
1235 "\t1: Ship to be operated on\r\n"
1236 "\t2, 3, etc : List of turrets to deactivate\r\n"},
1237 { OP_BEAM_LOCK_ALL, "beam-lock-all\r\n"
1238 "\tSets all beam weapons on the specified ship to be deactivated\r\n"},
1239 { OP_TURRET_FREE, "turret-free\r\n"
1240 "\tSets one or more turret weapons to allow firing for a given ship\r\n"
1241 "\t1: Ship to be operated on\r\n"
1242 "\t2, 3, etc : List of turrets to activate\r\n"},
1243 { OP_TURRET_FREE_ALL, "turret-free-all\r\n"
1244 "\tSets all turret weapons on the specified ship to be active\r\n"},
1245 { OP_TURRET_LOCK, "turret-lock\r\n"
1246 "\tSets one or more turret weapons to NOT allow firing for a given ship\r\n"
1247 "\t1: Ship to be operated on\r\n"
1248 "\t2, 3, etc : List of turrets to deactivate\r\n"},
1249 { OP_TURRET_LOCK_ALL, "turret-lock-all\r\n"
1250 "\tSets all turret weapons on the specified ship to be deactivated\r\n"},
1251 { OP_ADD_REMOVE_ESCORT, "add-remove-escort\r\n"
1252 "\tAdds or removes a ship from an escort list.\r\n"
1253 "\t1: Ship to be added or removed\r\n"
1254 "\t2: 0 to remove from the list, any positive value to add to the list\r\n"
1255 "NOTE : it _IS_ safe to add a ship which may already be on the list or remove\r\n"
1256 "a ship which is not on the list\r\n"},
1257 { OP_AWACS_SET_RADIUS, "awacs-set-radius\r\n"
1258 "\tSets the awacs radius for a given ship subsystem. NOTE : does not work properly in multiplayer\r\n"
1259 "\t1: Ship which has the awacs subsystem\r\n"
1260 "\t2: Awacs subsystem\r\n"
1261 "\t3: New radius\r\n"},
1262 { OP_SEND_MESSAGE_LIST, "send-message-list\r\n"
1263 "\tSends a series of delayed messages. All times are accumulated"
1264 "\t1:\tName of who the message is from.\r\n"
1265 "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\").\r\n"
1266 "\t3:\tName of message (from message editor).\r\n"
1267 "\t4:\tDelay in ms\r\n"
1268 "Use Add-Data for multiple messages"
1269 "IMPORTANT : each additional message in the list MUST HAVE 4 entries\r\n"
1270 "any message without the 4 proper fields will be ignore, as will any\r\n"
1271 "successive messages"},
1272 { OP_CAP_WAYPOINT_SPEED, "cap-waypoint-speed\r\n"
1273 "\tSets the maximum speed of a ship while flying waypoints.\r\n"
1274 "\t1: Ship name\r\n"
1275 "\t2: Maximum speed while flying waypoints\r\n"
1276 "\tNOTE: This will only work if the ship is already in the game\r\n"
1277 "\tNOTE: Set to -1 to reset\r\n"},
1278 { OP_TURRET_TAGGED_ONLY_ALL, "turret-tagged-only\r\n"
1279 "\tMakes turrets target and hence fire strictly at tagged objects\r\n"
1280 "\t1: Ship name\r\n"
1281 "\tNOTE: Will not stop a turret already firing at an untagged ship\r\n"},
1282 { OP_TURRET_TAGGED_CLEAR_ALL, "turret-tagged-clear\r\n"
1283 "\tRelaxes restriction on turrets targeting only tagged ships\r\n"
1284 "\t1: Ship name\r\n"},
1285 { OP_SECONDARIES_DEPLETED, "secondaries-depleted\r\n"
1286 "\tReturns true if ship is out secondary weapons\r\n"
1287 "\t1: Ship name\r\n"},
1288 { OP_SUBSYS_SET_RANDOM, "subsys-set-random\r\n"
1289 "\tSets ship subsystem strength in a given range\r\n"
1290 "\t1: Ship name\r\n"
1291 "\t2: Low range\r\n"
1292 "\t3: High range\r\n"
1293 "\t4: List of subsys names not to be randomized\r\n"},
1294 { OP_SUPERNOVA_START, "supernova-start\r\n"
1295 "\t1: Time in seconds until the supernova shockwave hits the player\r\n"},
1296 { OP_SHIELD_RECHARGE_PCT, "shield-recharge-pct\r\n"
1297 "\tReturns a percentage from 0 to 100\r\n"
1298 "\t1: Ship name\r\n" },
1299 { OP_ENGINE_RECHARGE_PCT, "engine-recharge-pct\r\n"
1300 "\tReturns a percentage from 0 to 100\r\n"
1301 "\t1: Ship name\r\n" },
1302 { OP_WEAPON_RECHARGE_PCT, "weapon-recharge-pct\r\n"
1303 "\tReturns a percentage from 0 to 100\r\n"
1304 "\t1: Ship name\r\n" },
1305 { OP_CARGO_NO_DEPLETE, "cargo-no-deplete\r\n"
1306 "\tCauses the named ship to have unlimited cargo.\r\n"
1307 "\tNote: only applies to BIG or HUGE ships\r\n"
1308 "Takes 1 or more arguments:\r\n"
1309 "\t1:\tName of one of the ships.\r\n"
1310 "\t2:\toptional: 1 disallow depletion, 0 allow depletion." },
1311 { OP_SHIELD_QUAD_LOW, "shield-quad-low\r\n"
1312 "\tReturns true if the specified ship has a shield quadrant below\r\n"
1313 "\tthe specified threshold percentage\r\n"
1314 "\t1: Ship name\r\n"
1315 "\t2: Percentage\r\n" },
1316 { OP_SECONDARY_AMMO_PCT, "secondary-ammo-pct\r\n"
1317 "\tReturns the percentage of ammo remaining in the specified bank (0 to 100)\r\n"
1318 "\t1: Ship name\r\n"
1319 "\t2: Bank to check (0, 1, 2 are legal banks. 3 will return the cumulative average for all banks" },
1320 { OP_IS_SECONDARY_SELECTED, "is-secondary-selected\r\n"
1321 "\tReturns true if the specified bank is selected (0 .. num_banks - 1)\r\n"
1322 "\t1: Ship name\r\n"
1323 "\t2: Bank to check (0 .. num_banks - 1)\r\n"},
1324 { OP_IS_PRIMARY_SELECTED, "is-primary-selected\r\n"
1325 "\tReturns true if the specified bank is selected (0 .. num_banks - 1)\r\n"
1326 "\t1: Ship name\r\n"
1327 "\t2: Bank to check (0 .. num_banks - 1)\r\n"},
1328 { OP_SPECIAL_WARP_DISTANCE, "special-warp-dist\r\n"
1329 "\tReturns distance to the plane of the knossos device in percent length of ship\r\n"
1330 "\t(ie, 100 means front of ship is 1 ship length from plane of knossos device)\r\n"
1331 "\t1: Ship name\r\n"},
1332 { OP_SET_SPECIAL_WARPOUT_NAME, "special-warpout-name\r\n"
1333 "\tSets the name of the knossos device to be used for warpout\r\n"
1334 "\t1: Ship name to exit\r\n"
1335 "\t2: Name of knossos device\r\n"},
1336 { OP_SHIP_VANISH, "ship-vanish\r\n"
1337 "\tMakes the named ship vanish (no log and vanish)\r\n"
1338 "\tSingle Player Only! Warning: This will cause ship exit not to be logged, so 'has-departed', etc. will not work\r\n"
1339 "\t1: List of ship names to vanish\r\n"},
1340 { OP_IS_SHIP_VISIBLE, "is-ship-visible\r\n"
1341 "\tCheck whether ship is visible on Player's radar\r\n"
1342 "\tSingle Player Only! Returns 0 - not visible, 1 - partially visible, 2 - fully visible.\r\n"
1343 "\t1: Name of ship to check\r\n"},
1344 { OP_TEAM_SCORE, "team-score\r\n"
1345 "\tGet the score of a multi team vs team game.\r\n"
1346 "\t1: Team index (1 for team 1 and 2 for team 2)\r\n"},
1350 struct op_menu_struct {
1355 { "Objectives", OP_CATAGORY_OBJECTIVE },
1356 { "Time", OP_CATAGORY_TIME },
1357 { "Logical", OP_CATAGORY_LOGICAL },
1358 { "Arithmetic", OP_CATAGORY_ARITHMETIC },
1359 { "Status", OP_CATAGORY_STATUS },
1360 { "Change", OP_CATAGORY_CHANGE },
1361 { "Conditionals", OP_CATAGORY_CONDITIONAL },
1362 { "Debugging", OP_CATAGORY_DEBUG },
1363 { "Ai goals", OP_CATAGORY_AI },
1364 { "Event/Goals", OP_CATAGORY_GOAL_EVENT },
1365 { "Training", OP_CATAGORY_TRAINING },
1368 int Num_op_menus = sizeof(op_menu) / sizeof(op_menu_struct);
1371 sexp_tree::sexp_tree()
1373 select_sexp_node = -1;
1377 m_p_image_list = NULL;
1382 // clears out the tree, so all the nodes are unused.
1383 void sexp_tree::clear_tree(char *op)
1388 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1389 nodes[i].type = SEXPT_UNUSED;
1394 set_node(allocate_node(-1), (SEXPT_OPERATOR | SEXPT_VALID), op);
1400 void sexp_tree::reset_handles()
1404 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1405 nodes[i].handle = NULL;
1408 // initializes and creates a tree from a given sexp startpoint.
1409 void sexp_tree::load_tree(int index, char *deflt)
1416 cur = allocate_node(-1);
1417 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), deflt); // setup a default tree if none
1422 if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) { // handle numbers allender likes to use so much..
1423 cur = allocate_node(-1);
1424 if (atoi(Sexp_nodes[index].text))
1425 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "true");
1427 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "false");
1433 // assumption: first token is an operator. I require this because it would cause problems
1434 // with child/parent relations otherwise, and it should be this way anyway, since the
1435 // return type of the whole sexp is boolean, and only operators can satisfy this.
1436 SDL_assert(Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR);
1437 load_branch(index, -1);
1441 void get_combined_variable_name(char *combined_name, const char *sexp_var_name)
1443 int sexp_var_index = get_index_sexp_variable_name(sexp_var_name);
1444 SDL_assert(sexp_var_index > -1);
1446 sprintf(combined_name, "%s(%s)", Sexp_variables[sexp_var_index].variable_name, Sexp_variables[sexp_var_index].text);
1451 // creates a tree from a given Sexp_nodes[] point under a given parent. Recursive.
1452 void sexp_tree::load_branch(int index, int parent)
1455 char combined_var_name[2*TOKEN_LENGTH + 2];
1457 while (index != -1) {
1458 SDL_assert(Sexp_nodes[index].type != SEXP_NOT_USED);
1459 if (Sexp_nodes[index].subtype == SEXP_ATOM_LIST) {
1460 load_branch(Sexp_nodes[index].first, parent); // do the sublist and continue
1462 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR) {
1463 cur = allocate_node(parent);
1464 if ((index == select_sexp_node) && !flag) { // translate sexp node to our node
1465 select_sexp_node = cur;
1469 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), Sexp_nodes[index].text);
1470 load_branch(Sexp_nodes[index].rest, cur); // operator is new parent now
1471 return; // 'rest' was just used, so nothing left to use.
1473 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) {
1474 cur = allocate_node(parent);
1475 if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
1476 get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
1477 set_node(cur, (SEXPT_VARIABLE | SEXPT_NUMBER | SEXPT_VALID), combined_var_name);
1479 set_node(cur, (SEXPT_NUMBER | SEXPT_VALID), Sexp_nodes[index].text);
1482 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_STRING) {
1483 cur = allocate_node(parent);
1484 if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
1485 get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
1486 set_node(cur, (SEXPT_VARIABLE | SEXPT_STRING | SEXPT_VALID), combined_var_name);
1488 set_node(cur, (SEXPT_STRING | SEXPT_VALID), Sexp_nodes[index].text);
1492 SDL_assert(0); // unknown and/or invalid sexp type
1494 if ((index == select_sexp_node) && !flag) { // translate sexp node to our node
1495 select_sexp_node = cur;
1499 index = Sexp_nodes[index].rest;
1505 int sexp_tree::query_false(int node)
1510 SDL_assert(node >= 0);
1511 SDL_assert(nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID));
1512 SDL_assert(nodes[node].next == -1); // must make this assumption or else it will confuse code!
1513 if (find_operator(nodes[node].text) == OP_FALSE){
1520 // builds an sexp of the tree and returns the index of it. This allocates sexp nodes.
1521 int sexp_tree::save_tree(int node)
1526 SDL_assert(node >= 0);
1527 SDL_assert(nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID));
1528 SDL_assert(nodes[node].next == -1); // must make this assumption or else it will confuse code!
1529 return save_branch(node);
1532 // get variable name from sexp_tree node .text
1533 void var_name_from_sexp_tree_text(char *var_name, const char *text)
1535 int var_name_length = strcspn(text, "(");
1536 SDL_assert(var_name_length < TOKEN_LENGTH - 1);
1538 strncpy(var_name, text, var_name_length);
1539 var_name[var_name_length] = '\0';
1542 #define NO_PREVIOUS_NODE -9
1543 // called recursively to save a tree branch and everything under it
1544 int sexp_tree::save_branch(int cur, int at_root)
1546 int start, node = -1, last = NO_PREVIOUS_NODE;
1547 char var_name_text[TOKEN_LENGTH];
1551 if (nodes[cur].type & SEXPT_OPERATOR) {
1552 node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, save_branch(nodes[cur].child));
1554 if ((nodes[cur].parent >= 0) && !at_root) {
1555 node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1);
1557 } else if (nodes[cur].type & SEXPT_NUMBER) {
1558 // allocate number, maybe variable
1559 if (nodes[cur].type & SEXPT_VARIABLE) {
1560 var_name_from_sexp_tree_text(var_name_text, nodes[cur].text);
1561 node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1);
1563 node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1);
1565 } else if (nodes[cur].type & SEXPT_STRING) {
1566 // allocate string, maybe variable
1567 if (nodes[cur].type & SEXPT_VARIABLE) {
1568 var_name_from_sexp_tree_text(var_name_text, nodes[cur].text);
1569 node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1);
1571 node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
1573 } else if (nodes[cur].type & SEXPT_STRING) {
1574 SDL_assert( !(nodes[cur].type & SEXPT_VARIABLE) );
1577 SDL_assert(0); // unknown and/or invalid type
1580 if (last == NO_PREVIOUS_NODE){
1582 } else if (last >= 0){
1583 Sexp_nodes[last].rest = node;
1587 SDL_assert(last != NO_PREVIOUS_NODE); // should be impossible
1588 cur = nodes[cur].next;
1597 // allocate a node. Remains used until freed.
1598 int sexp_tree::allocate_node()
1602 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1603 if (nodes[i].type == SEXPT_UNUSED) {
1604 nodes[i].type = SEXPT_UNINIT;
1605 nodes[i].parent = -1;
1606 nodes[i].child = -1;
1612 Error(LOCATION, "Out of sexp tree nodes!");
1616 // allocate a child node under 'parent'. Appends to end of list.
1617 int sexp_tree::allocate_node(int parent, int after)
1619 int i, index = allocate_node();
1622 i = nodes[parent].child;
1624 nodes[parent].child = index;
1627 while ((i != after) && (nodes[i].next != -1))
1630 nodes[index].next = nodes[i].next;
1631 nodes[i].next = index;
1635 nodes[index].parent = parent;
1639 // free a node and all it's children. Also clears pointers to it, if any.
1640 // node = node chain to free
1641 // cascade = 0: free just this node and children under it. (default)
1642 // !0: free this node and all siblings after it.
1644 void sexp_tree::free_node(int node, int cascade)
1648 // clear the pointer to node
1649 i = nodes[node].parent;
1650 SDL_assert(i != -1);
1651 if (nodes[i].child == node)
1652 nodes[i].child = nodes[node].next;
1656 while (nodes[i].next != -1) {
1657 if (nodes[i].next == node) {
1658 nodes[i].next = nodes[node].next;
1667 nodes[node].next = -1;
1669 // now free up the node and it's children
1673 // more simple node freer, which works recursively. It frees the given node and all siblings
1674 // that come after it, as well as all children of these. Doesn't clear any links to any of
1675 // these freed nodes, so make sure all links are broken first. (i.e. use free_node() if you can)
1677 void sexp_tree::free_node2(int node)
1679 SDL_assert(node != -1);
1680 SDL_assert(nodes[node].type != SEXPT_UNUSED);
1681 SDL_assert(total > 0);
1683 nodes[node].type = SEXPT_UNUSED;
1685 if (nodes[node].child != -1)
1686 free_node2(nodes[node].child);
1688 if (nodes[node].next != -1)
1689 free_node2(nodes[node].next);
1692 // initialize the data for a node. Should be called right after a new node is allocated.
1693 void sexp_tree::set_node(int node, int type, char *text)
1695 SDL_assert(type != SEXPT_UNUSED);
1696 SDL_assert(nodes[node].type != SEXPT_UNUSED);
1697 nodes[node].type = type;
1699 if (type & SEXPT_VARIABLE) {
1700 max_length = 2 * TOKEN_LENGTH + 2;
1702 max_length = TOKEN_LENGTH;
1704 SDL_assert(strlen(text) < max_length);
1705 strcpy(nodes[node].text, text);
1708 void sexp_tree::post_load()
1711 select_sexp_node = -1;
1714 // build or rebuild a CTreeCtrl object with the current tree data
1715 void sexp_tree::build_tree()
1718 select_sexp_node = -1;
1721 add_sub_tree(0, TVI_ROOT);
1724 // Create the CTreeCtrl tree from the tree data. The tree data should already be setup by
1726 void sexp_tree::add_sub_tree(int node, HTREEITEM root)
1731 SDL_assert(node >= 0 && node < MAX_SEXP_TREE_SIZE);
1732 node2 = nodes[node].child;
1734 // check for single argument operator case (prints as one line)
1735 /* if (node2 != -1 && nodes[node2].child == -1 && nodes[node2].next == -1) {
1736 sprintf(str, "%s %s", nodes[node].text, nodes[node2].text);
1737 nodes[node].handle = insert(str, root);
1738 nodes[node].flags = OPERAND | EDITABLE;
1739 nodes[node2].flags = COMBINED;
1743 // bitmap to draw in tree
1746 if (nodes[node].type & SEXPT_OPERATOR) {
1747 nodes[node].flags = OPERAND;
1748 bitmap = BITMAP_OPERATOR;
1750 if (nodes[node].type & SEXPT_VARIABLE) {
1751 nodes[node].flags = NOT_EDITABLE;
1752 bitmap = BITMAP_VARIABLE;
1754 nodes[node].flags = EDITABLE;
1755 bitmap = BITMAP_DATA;
1759 root = nodes[node].handle = insert(nodes[node].text, bitmap, bitmap, root);
1762 while (node != -1) {
1763 SDL_assert(node >= 0 && node < MAX_SEXP_TREE_SIZE);
1764 SDL_assert(nodes[node].type & SEXPT_VALID);
1765 if (nodes[node].type & SEXPT_OPERATOR) {
1766 add_sub_tree(node, root);
1769 SDL_assert(nodes[node].child == -1);
1770 if (nodes[node].type & SEXPT_VARIABLE) {
1771 nodes[node].handle = insert(nodes[node].text, BITMAP_VARIABLE, BITMAP_VARIABLE, root);
1772 nodes[node].flags = NOT_EDITABLE;
1774 nodes[node].handle = insert(nodes[node].text, BITMAP_DATA, BITMAP_DATA, root);
1775 nodes[node].flags = EDITABLE;
1779 node = nodes[node].next;
1783 void sexp_tree::setup_selected(HTREEITEM h)
1790 item_handle = GetSelectedItem();
1792 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1793 if (nodes[i].handle == item_handle) {
1799 int sexp_tree::get_ambiguous_type(int parent)
1802 int first_arg_index = get_modify_variable_first_arg_index();
1803 int sexp_var_index = get_tree_name_to_sexp_variable_index(nodes[first_arg_index].text);
1804 SDL_assert(sexp_var_index != -1);
1806 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
1809 return OPF_AMBIGUOUS;
1814 int sexp_tree::get_modify_variable_first_arg_index()
1818 SDL_assert( item_index >= 0);
1820 // get parent and check "modify-variable"
1821 index = nodes[item_index].parent;
1822 SDL_assert( index != -1 );
1823 SDL_assert( !(stricmp(nodes[index].text, "modify-variable")) );
1825 // get first child and verify type variable
1826 index = nodes[index].child;
1827 SDL_assert( index != -1 );
1828 SDL_assert( nodes[index].type & SEXPT_VARIABLE);
1833 // handler for right mouse button clicks.
1834 void sexp_tree::right_clicked(int mode)
1837 int i, j, z, count, op, add_type, replace_type, type;
1838 sexp_list_item *list;
1841 POINT click_point, mouse;
1842 CMenu menu, *mptr, *popup_menu, *add_data_menu = NULL, *replace_data_menu = NULL;
1843 CMenu *add_op_menu, add_op_submenu[MAX_OP_MENUS];
1844 CMenu *replace_op_menu, replace_op_submenu[MAX_OP_MENUS];
1845 CMenu *insert_op_menu, insert_op_submenu[MAX_OP_MENUS];
1846 CMenu *replace_variable_menu = NULL;
1849 add_instance = replace_instance = -1;
1850 SDL_assert(Num_operators <= MAX_OPERATORS);
1851 SDL_assert(Num_op_menus < MAX_OP_MENUS);
1852 GetCursorPos(&mouse);
1853 click_point = mouse;
1854 ScreenToClient(&click_point);
1855 h = HitTest(CPoint(click_point), &_flags); // find out what they clicked on
1856 if (h && menu.LoadMenu(IDR_MENU_EDIT_SEXP_TREE)) {
1858 popup_menu = menu.GetSubMenu(0);
1859 ASSERT(popup_menu != NULL);
1860 SelectDropTarget(h);
1862 add_op_menu = replace_op_menu = insert_op_menu = NULL;
1864 // get pointers to several key popup menus we'll need to modify
1865 i = popup_menu->GetMenuItemCount();
1867 if ( (mptr = popup_menu->GetSubMenu(i)) > 0 ) {
1868 popup_menu->GetMenuString(i, buf, sizeof(buf), MF_BYPOSITION);
1870 if (!stricmp(buf, "add operator")) {
1873 } else if (!stricmp(buf, "replace operator")) {
1874 replace_op_menu = mptr;
1876 } else if (!stricmp(buf, "add data")) {
1877 add_data_menu = mptr;
1879 } else if (!stricmp(buf, "replace data")) {
1880 replace_data_menu = mptr;
1882 } else if (!stricmp(buf, "insert operator")) {
1883 insert_op_menu = mptr;
1885 } else if (!stricmp(buf, "replace variable")) {
1886 replace_variable_menu = mptr;
1891 // add popup menus for all the operator catagories
1892 for (i=0; i<Num_op_menus; i++) {
1893 add_op_submenu[i].CreatePopupMenu();
1894 replace_op_submenu[i].CreatePopupMenu();
1895 insert_op_submenu[i].CreatePopupMenu();
1896 add_op_menu->AppendMenu(MF_POPUP, (UINT) add_op_submenu[i].m_hMenu, op_menu[i].name);
1897 replace_op_menu->AppendMenu(MF_POPUP, (UINT) replace_op_submenu[i].m_hMenu, op_menu[i].name);
1898 insert_op_menu->AppendMenu(MF_POPUP, (UINT) insert_op_submenu[i].m_hMenu, op_menu[i].name);
1901 // get rid of the placeholders we needed to ensure popup menus stayed popup menus,
1902 // i.e. MSDEV will convert empty popup menus into normal menu items.
1903 add_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1904 replace_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1905 insert_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1906 replace_variable_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1911 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
1912 if (nodes[i].handle == h) {
1918 // Do SEXP_VARIABLE stuff here.
1919 if (m_mode != MODE_EVENTS) {
1920 // only allow variables in event mode
1921 menu.EnableMenuItem(ID_SEXP_TREE_ADD_VARIABLE, MF_GRAYED);
1922 menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED);
1924 menu.EnableMenuItem(ID_SEXP_TREE_ADD_VARIABLE, MF_ENABLED);
1925 menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_ENABLED);
1927 // check not root (-1)
1928 if (item_index >= 0) {
1929 // get type of sexp_tree item clicked on
1930 int type = get_type(h);
1932 int parent = nodes[item_index].parent;
1934 op = identify_operator(nodes[parent].text);
1935 SDL_assert(op >= 0);
1936 int first_arg = nodes[parent].child;
1938 // get arg count of item to replace
1940 int temp = first_arg;
1941 while (temp != item_index) {
1943 temp = nodes[temp].next;
1945 // DB - added 3/4/99
1951 int op_type = query_operator_argument_type(op, Replace_count); // check argument type at this position
1953 // special case dont allow replace data for variable names
1954 if (op_type != OPF_AMBIGUOUS) {
1956 if ( (type & SEXPT_STRING) || (type & SEXPT_NUMBER) ) {
1958 int max_sexp_vars = MAX_SEXP_VARIABLES;
1959 // prevent collisions in id numbers: ID_VARIABLE_MENU + 512 = ID_ADD_MENU
1960 SDL_assert(max_sexp_vars < 512);
1962 for (int idx=0; idx<max_sexp_vars; idx++) {
1963 if (Sexp_variables[idx].type & SEXP_VARIABLE_SET) {
1964 UINT flag = MF_STRING | MF_GRAYED;
1965 // maybe gray flag MF_GRAYED
1967 // get type -- gray "string" or number accordingly
1968 if ( type & SEXPT_STRING ) {
1969 if ( Sexp_variables[idx].type & SEXP_VARIABLE_STRING ) {
1972 } else if ( type & SEXPT_NUMBER ) {
1973 if ( Sexp_variables[idx].type & SEXP_VARIABLE_NUMBER ) {
1978 // if modify-variable and changing variable, enable all variables
1979 if (op_type == OPF_VARIABLE_NAME) {
1980 Modify_variable = 1;
1983 Modify_variable = 0;
1987 // append list of variable names and values
1988 // set id as ID_VARIABLE_MENU + idx
1989 sprintf(buf, "%s(%s)", Sexp_variables[idx].variable_name, Sexp_variables[idx].text);
1991 replace_variable_menu->AppendMenu(flag, (ID_VARIABLE_MENU + idx), buf);
1999 // cant modify if no variables
2000 if (sexp_variable_count() == 0) {
2001 menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED);
2005 // add operator menu items to the various catagory submenus they belong in
2006 for (i=0; i<Num_operators; i++) {
2007 for (j=0; j<Num_op_menus; j++) {
2008 if (op_menu[j].id == (Operators[i].value & OP_CATAGORY_MASK)) {
2011 switch (Operators[i].value) {
2012 case OP_WAS_PROMOTION_GRANTED:
2013 case OP_WAS_MEDAL_GRANTED:
2014 case OP_GRANT_PROMOTION:
2015 case OP_GRANT_MEDAL:
2016 case OP_TECH_ADD_SHIP:
2017 case OP_TECH_ADD_WEAPON:
2018 j = Num_op_menus; // don't allow these operators in final release
2021 if (j < Num_op_menus) {
2022 add_op_submenu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value, Operators[i].text);
2023 replace_op_submenu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value | OP_REPLACE_FLAG, Operators[i].text);
2024 insert_op_submenu[j].AppendMenu(MF_STRING, Operators[i].value | OP_INSERT_FLAG, Operators[i].text);
2027 break; // only 1 category valid
2032 // find local index (i) of current item (from it's handle)
2033 SelectItem(item_handle = h);
2034 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
2035 if (nodes[i].handle == h) {
2040 // special case: item is a ROOT node, and a label that can be edited (not an item in the sexp tree)
2041 if ((item_index == -1) && (m_mode & ST_LABELED_ROOT)) {
2042 if (m_mode & ST_ROOT_EDITABLE) {
2043 menu.EnableMenuItem(ID_EDIT_TEXT, MF_ENABLED);
2045 menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED);
2048 // disable copy, insert op
2049 menu.EnableMenuItem(ID_EDIT_COPY, MF_GRAYED);
2050 for (j=0; j<Num_operators; j++) {
2051 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2054 gray_menu_tree(popup_menu);
2055 popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this);
2059 SDL_assert(item_index != -1); // handle not found, which should be impossible.
2060 if (!(nodes[item_index].flags & EDITABLE)) {
2061 menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED);
2064 if (nodes[item_index].parent == -1) { // root node
2065 menu.EnableMenuItem(ID_DELETE, MF_GRAYED); // can't delete the root item.
2068 /* if ((nodes[item_index].flags & OPERAND) && (nodes[item_index].flags & EDITABLE)) // expandable?
2069 menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
2071 z = nodes[item_index].child;
2072 if (z != -1 && nodes[z].next == -1 && nodes[z].child == -1)
2073 menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
2075 z = nodes[nodes[item_index].parent].child;
2076 if (z != -1 && nodes[z].next == -1 && nodes[z].child == -1)
2077 menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);*/
2079 // change enabled status of 'add' type menu options.
2081 if (nodes[item_index].flags & OPERAND) {
2082 add_type = OPR_STRING;
2083 int child = nodes[item_index].child;
2084 Add_count = count_args(child);
2085 op = identify_operator(nodes[item_index].text);
2086 SDL_assert(op >= 0);
2088 // get listing of valid argument values and add to menus
2089 type = query_operator_argument_type(op, Add_count);
2090 list = get_listing_opf(type, item_index, Add_count);
2092 sexp_list_item *ptr;
2098 // enable operators with correct return type
2099 menu.EnableMenuItem(Operators[ptr->op].value, MF_ENABLED);
2103 if ( (data_idx + 3) % 30) {
2104 add_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text);
2106 add_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text);
2115 // special handling for the non-string formats
2116 if (type == OPF_NONE) { // an argument can't be added
2119 } else if (type == OPF_NULL) { // arguments with no return values
2120 add_type = OPR_NULL;
2122 } else if (type == OPF_NUMBER) { // takes numbers
2123 add_type = OPR_NUMBER;
2124 menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED);
2126 } else if (type == OPF_POSITIVE) { // takes non-negative numbers
2127 add_type = OPR_POSITIVE;
2128 menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED);
2130 } else if (type == OPF_BOOL) { // takes true/false bool values
2131 add_type = OPR_BOOL;
2133 } else if (type == OPF_AI_GOAL) {
2134 add_type = OPR_AI_GOAL;
2137 // add_type unchanged from above
2138 if (add_type == OPR_STRING) {
2139 menu.EnableMenuItem(ID_ADD_STRING, MF_ENABLED);
2145 // disable operators that do not have arguments available
2146 for (j=0; j<Num_operators; j++) {
2147 if (!query_default_argument_available(j)) {
2148 menu.EnableMenuItem(Operators[j].value, MF_GRAYED);
2153 // change enabled status of 'replace' type menu options.
2155 int parent = nodes[item_index].parent;
2157 replace_type = OPR_STRING;
2158 op = identify_operator(nodes[parent].text);
2159 SDL_assert(op >= 0);
2160 int first_arg = nodes[parent].child;
2161 count = count_args(nodes[parent].child);
2163 // already at minimum number of arguments?
2164 if (count <= Operators[op].min) {
2165 menu.EnableMenuItem(ID_DELETE, MF_GRAYED);
2168 // get arg count of item to replace
2170 int temp = first_arg;
2171 while (temp != item_index) {
2173 temp = nodes[temp].next;
2175 // DB - added 3/4/99
2181 // maybe gray delete
2182 for (i=Replace_count+1; i<count; i++) {
2183 if (query_operator_argument_type(op, i-1) != query_operator_argument_type(op, i)) {
2184 menu.EnableMenuItem(ID_DELETE, MF_GRAYED);
2189 type = query_operator_argument_type(op, Replace_count); // check argument type at this position
2191 // special case reset type for ambiguous
2192 if (type == OPF_AMBIGUOUS) {
2193 type = get_ambiguous_type(parent);
2196 list = get_listing_opf(type, parent, Replace_count);
2198 // special case dont allow replace data for variable names
2199 if ( (type != OPF_VARIABLE_NAME) && list) {
2200 sexp_list_item *ptr;
2206 menu.EnableMenuItem(Operators[ptr->op].value | OP_REPLACE_FLAG, MF_ENABLED);
2209 if ( (data_idx + 3) % 30)
2210 replace_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text);
2212 replace_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text);
2220 if (type == OPF_NONE) { // takes no arguments
2223 } else if (type == OPF_NUMBER) { // takes numbers
2224 replace_type = OPR_NUMBER;
2225 menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
2227 } else if (type == OPF_POSITIVE) { // takes non-negative numbers
2228 replace_type = OPR_POSITIVE;
2229 menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
2231 } else if (type == OPF_BOOL) { // takes true/false bool values
2232 replace_type = OPR_BOOL;
2234 } else if (type == OPF_NULL) { // takes operator that doesn't return a value
2235 replace_type = OPR_NULL;
2237 } else if (type == OPF_AI_GOAL) {
2238 replace_type = OPR_AI_GOAL;
2241 // default to string
2242 if (replace_type == OPR_STRING) {
2243 menu.EnableMenuItem(ID_REPLACE_STRING, MF_ENABLED);
2246 // modify string or number if (modify_variable)
2247 if ( !stricmp(Operators[op].text, "modify-variable") ) {
2248 int modify_type = get_modify_variable_type();
2250 if (modify_type == OPF_NUMBER) {
2251 menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
2252 menu.EnableMenuItem(ID_REPLACE_STRING, MF_GRAYED);
2254 // no change for string type
2259 } else { // top node, so should be a Boolean type.
2260 if (m_mode == MODE_EVENTS) { // return type should be null
2261 replace_type = OPR_NULL;
2262 for (j=0; j<Num_operators; j++)
2263 if (query_operator_return_type(j) == OPR_NULL)
2264 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_ENABLED);
2267 replace_type = OPR_BOOL;
2268 for (j=0; j<Num_operators; j++)
2269 if (query_operator_return_type(j) == OPR_BOOL)
2270 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_ENABLED);
2274 for (j=0; j<Num_operators; j++)
2275 if (!query_default_argument_available(j))
2276 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_GRAYED);
2278 // change enabled status of 'insert' type menu options.
2279 z = nodes[item_index].parent;
2280 SDL_assert(z >= -1);
2282 op = identify_operator(nodes[z].text);
2283 SDL_assert(op != -1);
2286 while (j != item_index) {
2291 type = query_operator_argument_type(op, count); // check argument type at this position
2294 if (m_mode == MODE_EVENTS)
2300 for (j=0; j<Num_operators; j++) {
2301 z = query_operator_return_type(j);
2302 if (!sexp_query_type_match(type, z) || (Operators[j].min < 1))
2303 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2305 z = query_operator_argument_type(j, 0);
2306 if ((type == OPF_NUMBER) && (z == OPF_POSITIVE))
2310 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2313 for (j=0; j<Num_operators; j++)
2314 if (!query_default_argument_available(j))
2315 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2317 // disable non campaign operators if in campaign mode
2318 for (j=0; j<Num_operators; j++) {
2320 if (m_mode == MODE_CAMPAIGN) {
2321 if (Operators[j].value & OP_NONCAMPAIGN_FLAG)
2325 if (Operators[j].value & OP_CAMPAIGN_ONLY_FLAG)
2330 menu.EnableMenuItem(Operators[j].value, MF_GRAYED);
2331 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_GRAYED);
2332 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2336 if ((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)) {
2337 SDL_assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
2339 if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
2340 j = find_operator(CTEXT(Sexp_clipboard));
2342 z = query_operator_return_type(j);
2344 if ((z == OPR_POSITIVE) && (replace_type == OPR_NUMBER))
2347 if (replace_type == z)
2348 menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2350 z = query_operator_return_type(j);
2351 if ((z == OPR_POSITIVE) && (add_type == OPR_NUMBER))
2355 menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2357 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
2358 if ((replace_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1))
2359 menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2361 else if (replace_type == OPR_NUMBER)
2362 menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2364 if ((add_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1))
2365 menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2367 else if (add_type == OPR_NUMBER)
2368 menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2370 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
2371 if (replace_type == OPR_STRING)
2372 menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2374 if (add_type == OPR_STRING)
2375 menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2378 Int3(); // unknown and/or invalid sexp type
2381 if (!(menu.GetMenuState(ID_DELETE, MF_BYCOMMAND) & MF_GRAYED))
2382 menu.EnableMenuItem(ID_EDIT_CUT, MF_ENABLED);
2384 gray_menu_tree(popup_menu);
2385 popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this);
2389 // counts the number of arguments an operator has. Call this with the node of the first
2390 // argument of the operator
2391 int sexp_tree::count_args(int node)
2395 while (node != -1) {
2397 node = nodes[node].next;
2403 // identify what type of argument this is. You call it with the node of the first argument
2404 // of an operator. It will search through enough of the arguments to determine what type of
2406 int sexp_tree::identify_arg_type(int node)
2410 while (node != -1) {
2411 SDL_assert(nodes[node].type & SEXPT_VALID);
2412 switch (SEXPT_TYPE(nodes[node].type)) {
2413 case SEXPT_OPERATOR:
2414 type = find_operator(nodes[node].text);
2416 return query_operator_return_type(type);
2421 case SEXPT_STRING: // either a ship or a wing
2422 type = SEXP_ATOM_STRING;
2423 break; // don't return, because maybe we can narrow selection down more.
2426 node = nodes[node].next;
2432 // determine if an item should be editable. This doesn't actually edit the label.
2433 int sexp_tree::edit_label(HTREEITEM h)
2437 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
2438 if (nodes[i].handle == h) {
2443 // Check if tree root
2444 if (i == MAX_SEXP_TREE_SIZE) {
2445 if (m_mode & ST_ROOT_EDITABLE) {
2452 // Operators are editable
2453 if (nodes[i].type & SEXPT_OPERATOR) {
2457 // Variables must be edited through dialog box
2458 if (nodes[i].type & SEXPT_VARIABLE) {
2462 // Don't edit if not flaged as editable
2463 if (!(nodes[i].flags & EDITABLE)) {
2467 // Otherwise, allow editing
2471 if (nodes[i].flags & OPERAND) {
2472 data = nodes[i].child;
2474 SetItemText(h, nodes[i].text);
2475 nodes[i].flags = OPERAND;
2476 item_handle = nodes[data].handle = insert(nodes[data].text, nodes[data].type, nodes[data].flags, h);
2477 nodes[data].flags = EDITABLE;
2478 Expand(h, TVE_EXPAND);
2479 SelectItem(item_handle);
2485 int sexp_tree::end_label_edit(HTREEITEM h, char *str)
2487 int len, node, r = 1;
2493 for (node=0; node<MAX_SEXP_TREE_SIZE; node++)
2494 if (nodes[node].handle == h)
2497 if (node == MAX_SEXP_TREE_SIZE) {
2498 if (m_mode == MODE_EVENTS) {
2499 item_index = GetItemData(h);
2500 SDL_assert(Event_editor_dlg);
2501 node = Event_editor_dlg->handler(ROOT_RENAMED, item_index, str);
2505 Int3(); // root labels shouldn't have been editable!
2508 SDL_assert(node < MAX_SEXP_TREE_SIZE);
2509 if (nodes[node].type & SEXPT_OPERATOR) {
2510 str = match_closest_operator(str, node);
2511 SetItemText(h, str);
2513 add_or_replace_operator(identify_operator(str), 1);
2517 // Error checking would not hurt here
2519 if (len >= TOKEN_LENGTH)
2520 len = TOKEN_LENGTH - 1;
2522 strncpy(nodes[node].text, str, len);
2523 nodes[node].text[len] = 0;
2525 /* node = nodes[node].parent;
2527 child = nodes[node].child;
2528 if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) {
2529 merge_operator(child);
2537 // Check if 'op' is a valid operator match for operator format (OPF) 'type'. Returns true if it
2538 // is, false if not.
2540 int sexp_tree::check_operator_validity(int op, int type)
2544 rtype = query_operator_return_type(op);
2547 return ( (rtype == OPR_NUMBER) || (rtype == OPR_POSITIVE) );
2550 return (rtype == OPR_POSITIVE);
2553 return (rtype == OPR_BOOL);
2556 return (rtype == OPR_NULL);
2559 return (rtype == OPR_AI_GOAL);
2565 // Look for the valid operator that is the closest match for 'str' and return the operator
2566 // number of it. What operators are valid is determined by 'node', and an operator is valid
2567 // if it is allowed to fit at position 'node'
2569 char *sexp_tree::match_closest_operator(char *str, int node)
2571 int z, n, i, op, arg_num, type;
2572 char *sub_best = NULL, *best = NULL;
2574 z = nodes[node].parent;
2579 op = identify_operator(nodes[z].text);
2583 // determine which argument we are of the parent
2592 type = query_operator_argument_type(op, arg_num); // check argument type at this position
2593 for (i=0; i<Num_operators; i++) {
2594 if (check_operator_validity(i, type)) {
2595 if ( (stricmp(str, Operators[i].text) <= 0) && (!best || (stricmp(str, best) < 0)) )
2596 best = Operators[i].text;
2598 if ( !sub_best || (stricmp(Operators[i].text, sub_best) > 0) )
2599 sub_best = Operators[i].text;
2604 best = sub_best; // no best found, use our plan #2 best found.
2606 SDL_assert(best); // we better have some valid operator at this point.
2612 if (nodes[node].flags == EDITABLE) // data
2613 node = nodes[node].parent;
2616 child = nodes[node].child;
2617 if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) {
2618 sprintf(buf, "%s %s", nodes[node].text, nodes[child].text);
2619 SetItemText(nodes[node].handle, buf);
2620 nodes[node].flags = OPERAND | EDITABLE;
2621 nodes[child].flags = COMBINED;
2622 DeleteItem(nodes[child].handle);
2623 nodes[child].handle = NULL;
2629 // this really only handles messages generated by the right click popup menu
2630 BOOL sexp_tree::OnCommand(WPARAM wParam, LPARAM lParam)
2632 int i, z, id, node, op, type;
2633 sexp_list_item *list, *ptr;
2636 if ((item_index >= 0) && (item_index < total))
2637 item_handle = nodes[item_index].handle;
2639 id = LOWORD(wParam);
2643 if (id == ID_SEXP_TREE_ADD_VARIABLE) {
2644 CAddVariableDlg dlg;
2647 if ( dlg.m_create ) {
2651 if ( dlg.m_type_number ) {
2652 type = SEXP_VARIABLE_NUMBER;
2654 type = SEXP_VARIABLE_STRING;
2658 sexp_add_variable(dlg.m_default_value, dlg.m_variable_name, type);
2661 sexp_variable_sort();
2667 if (id == ID_SEXP_TREE_MODIFY_VARIABLE) {
2668 CModifyVariableDlg dlg;
2670 // get sexp_variable index for item index
2671 dlg.m_start_index = get_item_index_to_var_index();
2673 // get pointer to tree
2674 dlg.m_p_sexp_tree = this;
2678 SDL_assert( !(dlg.m_deleted && dlg.m_do_modify) );
2680 if (dlg.m_deleted) {
2681 // find index in sexp_variable list
2682 int sexp_var_index = get_index_sexp_variable_name(dlg.m_cur_variable_name);
2683 SDL_assert(sexp_var_index != -1);
2686 sexp_variable_delete(sexp_var_index);
2689 sexp_variable_sort();
2691 // delete from sexp_tree, replacing with "number" or "string" as needed
2692 // further error checking from add_data()
2693 delete_sexp_tree_variable(dlg.m_cur_variable_name);
2698 if (dlg.m_do_modify) {
2699 // check sexp_tree -- warn on type
2700 // find index and change either (1) name, (2) type, (3) value
2701 int sexp_var_index = get_index_sexp_variable_name(dlg.m_old_var_name);
2702 SDL_assert(sexp_var_index != -1);
2704 // save old name, since name may be modified
2705 char old_name[TOKEN_LENGTH];
2706 strcpy(old_name, Sexp_variables[sexp_var_index].variable_name);
2710 if (dlg.m_type_number) {
2711 type = SEXP_VARIABLE_NUMBER;
2713 type = SEXP_VARIABLE_STRING;
2716 // update sexp_variable
2717 sexp_fred_modify_variable(dlg.m_default_value, dlg.m_cur_variable_name, sexp_var_index, type);
2720 modify_sexp_tree_variable(old_name, sexp_var_index);
2722 // Don't sort until after modify, since modify uses index
2723 if (dlg.m_modified_name) {
2724 sexp_variable_sort();
2735 // check if REPLACE_VARIABLE_MENU
2736 if ( (id >= ID_VARIABLE_MENU) && (id < ID_VARIABLE_MENU + 511)) {
2738 SDL_assert(item_index >= 0);
2740 // get index into list of type valid variables
2741 int var_idx = id - ID_VARIABLE_MENU;
2742 SDL_assert( (var_idx >= 0) && (var_idx < MAX_SEXP_VARIABLES) );
2744 int type = get_type(item_handle);
2745 SDL_assert( (type & SEXPT_NUMBER) || (type & SEXPT_STRING) );
2747 // dont do type check for modify-variable
2748 if (Modify_variable) {
2749 if (Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER) {
2750 type = SEXPT_NUMBER;
2751 } else if (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) {
2752 type = SEXPT_STRING;
2754 Int3(); // unknown type
2759 // verify type in tree is same as type in Sexp_variables array
2760 if (type & SEXPT_NUMBER) {
2761 SDL_assert(Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER);
2764 if (type & SEXPT_STRING) {
2765 SDL_assert( (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) );
2770 replace_variable_data(var_idx, (type | SEXPT_VARIABLE));
2776 if ((id >= ID_ADD_MENU) && (id < ID_ADD_MENU + 511)) {
2777 SDL_assert(item_index >= 0);
2778 op = identify_operator(nodes[item_index].text);
2779 SDL_assert(op >= 0);
2781 type = query_operator_argument_type(op, Add_count);
2782 list = get_listing_opf(type, item_index, Add_count);
2793 SDL_assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0));
2794 expand_operator(item_index);
2795 add_data(ptr->text, ptr->type);
2800 if ((id >= ID_REPLACE_MENU) && (id < ID_REPLACE_MENU + 511)) {
2801 SDL_assert(item_index >= 0);
2802 SDL_assert(nodes[item_index].parent >= 0);
2803 op = identify_operator(nodes[nodes[item_index].parent].text);
2804 SDL_assert(op >= 0);
2806 type = query_operator_argument_type(op, Replace_count); // check argument type at this position
2807 list = get_listing_opf(type, nodes[item_index].parent, Replace_count);
2810 id -= ID_REPLACE_MENU;
2818 SDL_assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0));
2819 expand_operator(item_index);
2820 replace_data(ptr->text, ptr->type);
2825 for (op=0; op<Num_operators; op++) {
2826 if (id == Operators[op].value) {
2827 add_or_replace_operator(op);
2831 if (id == (Operators[op].value | OP_REPLACE_FLAG)) {
2832 add_or_replace_operator(op, 1);
2836 if (id == (Operators[op].value | OP_INSERT_FLAG)) {
2839 z = nodes[item_index].parent;
2840 flags = nodes[item_index].flags;
2841 node = allocate_node(z, item_index);
2842 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text);
2843 nodes[node].flags = flags;
2845 h = nodes[z].handle;
2848 h = GetParentItem(nodes[item_index].handle);
2849 if (m_mode == MODE_GOALS) {
2850 SDL_assert(Goal_editor_dlg);
2851 Goal_editor_dlg->insert_handler(item_index, node);
2852 SetItemData(h, node);
2854 } else if (m_mode == MODE_EVENTS) {
2855 SDL_assert(Event_editor_dlg);
2856 Event_editor_dlg->insert_handler(item_index, node);
2857 SetItemData(h, node);
2859 } else if (m_mode == MODE_CAMPAIGN) {
2860 Campaign_tree_formp->insert_handler(item_index, node);
2861 SetItemData(h, node);
2869 item_handle = nodes[node].handle = insert(Operators[op].text, BITMAP_OPERATOR, BITMAP_OPERATOR, h, nodes[item_index].handle);
2870 move_branch(item_index, node);
2873 for (i=1; i<Operators[op].min; i++)
2874 add_default_operator(op, i);
2876 Expand(item_handle, TVE_EXPAND);
2884 // If a clipboard already exist, unmark it as persistent and free old clipboard
2885 if (Sexp_clipboard != -1) {
2886 sexp_unmark_persistent(Sexp_clipboard);
2887 free_sexp2(Sexp_clipboard);
2890 // Allocate new clipboard and mark persistent
2891 Sexp_clipboard = save_branch(item_index, 1);
2892 sexp_mark_persistent(Sexp_clipboard);
2896 // the following assumptions are made..
2897 SDL_assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
2898 SDL_assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
2900 if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
2901 expand_operator(item_index);
2902 replace_operator(CTEXT(Sexp_clipboard));
2903 if (Sexp_nodes[Sexp_clipboard].rest != -1) {
2904 load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index);
2905 i = nodes[item_index].child;
2907 add_sub_tree(i, nodes[item_index].handle);
2912 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
2913 SDL_assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2914 expand_operator(item_index);
2915 replace_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID));
2917 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
2918 SDL_assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2919 expand_operator(item_index);
2920 replace_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
2923 SDL_assert(0); // unknown and/or invalid sexp type
2927 case ID_EDIT_PASTE_SPECIAL: // add paste, instead of replace.
2928 // the following assumptions are made..
2929 SDL_assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
2930 SDL_assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
2932 if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
2933 expand_operator(item_index);
2934 add_operator(CTEXT(Sexp_clipboard));
2935 if (Sexp_nodes[Sexp_clipboard].rest != -1) {
2936 load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index);
2937 i = nodes[item_index].child;
2939 add_sub_tree(i, nodes[item_index].handle);
2944 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
2945 SDL_assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2946 expand_operator(item_index);
2947 add_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID));
2949 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
2950 SDL_assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2951 expand_operator(item_index);
2952 add_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
2955 SDL_assert(0); // unknown and/or invalid sexp type
2959 /* case ID_SPLIT_LINE:
2960 if ((nodes[item_index].flags & OPERAND) && (nodes[item_index].flags & EDITABLE)) // expandable?
2961 expand_operator(item_index);
2963 merge_operator(item_index);
2968 expand_branch(item_handle);
2972 if (edit_label(item_handle)) {
2974 EditLabel(item_handle);
2979 case ID_ADD_STRING: {
2982 node = add_data("string", (SEXPT_STRING | SEXPT_VALID));
2983 EditLabel(nodes[node].handle);
2987 case ID_ADD_NUMBER: {
2990 node = add_data("number", (SEXPT_NUMBER | SEXPT_VALID));
2991 EditLabel(nodes[node].handle);
2996 if (Sexp_clipboard != -1) {
2997 sexp_unmark_persistent(Sexp_clipboard);
2998 free_sexp2(Sexp_clipboard);
3001 Sexp_clipboard = save_branch(item_index, 1);
3002 sexp_mark_persistent(Sexp_clipboard);
3003 // fall through to ID_DELETE case.
3009 if ((m_mode & ST_ROOT_DELETABLE) && (item_index == -1)) {
3010 item_index = GetItemData(item_handle);
3011 if (m_mode == MODE_GOALS) {
3012 SDL_assert(Goal_editor_dlg);
3013 node = Goal_editor_dlg->handler(ROOT_DELETED, item_index);
3015 } else if (m_mode == MODE_EVENTS) {
3016 SDL_assert(Event_editor_dlg);
3017 node = Event_editor_dlg->handler(ROOT_DELETED, item_index);
3020 SDL_assert(m_mode == MODE_CAMPAIGN);
3021 node = Campaign_tree_formp->handler(ROOT_DELETED, item_index);
3024 SDL_assert(node >= 0);
3026 DeleteItem(item_handle);
3031 SDL_assert(item_index >= 0);
3032 h = GetParentItem(item_handle);
3033 parent = nodes[item_index].parent;
3034 if ((parent == -1) && (m_mode == MODE_EVENTS))
3035 Int3(); // no longer used, temporary to check if called still.
3037 SDL_assert(parent != -1 && nodes[parent].handle == h);
3038 free_node(item_index);
3039 DeleteItem(item_handle);
3041 node = nodes[parent].child;
3042 /* if (node != -1 && nodes[node].next == -1 && nodes[node].child == -1) {
3043 sprintf(buf, "%s %s", nodes[parent].text, nodes[node].text);
3044 SetItem(h, TVIF_TEXT, buf, 0, 0, 0, 0, 0);
3045 nodes[parent].flags = OPERAND | EDITABLE;
3046 nodes[node].flags = COMBINED;
3047 DeleteItem(nodes[node].handle);
3055 return CTreeCtrl::OnCommand(wParam, lParam);
3058 // adds to or replaces (based on passed in flag) the current operator
3059 void sexp_tree::add_or_replace_operator(int op, int replace_flag)
3061 int i, op_index, op2;
3063 op_index = item_index;
3065 if (nodes[item_index].flags & OPERAND) { // are both operators?
3066 op2 = identify_operator(nodes[item_index].text);
3067 SDL_assert(op2 >= 0);
3068 i = count_args(nodes[item_index].child);
3069 if ((i >= Operators[op].min) && (i <= Operators[op].max)) { // are old num args valid?
3071 if (query_operator_argument_type(op2, i) != query_operator_argument_type(op, i)) // does each arg match expected type?
3074 if (i < 0) { // everything is ok, so we can keep old arguments with new operator
3075 set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text);
3076 SetItemText(nodes[item_index].handle, Operators[op].text);
3077 nodes[item_index].flags = OPERAND;
3083 replace_operator(Operators[op].text);
3086 add_operator(Operators[op].text);
3088 // fill in all the required (minimum) arguments with default values
3089 for (i=0; i<Operators[op].min; i++)
3090 add_default_operator(op, i);
3092 Expand(item_handle, TVE_EXPAND);
3095 // initialize node, type operator
3097 void sexp_list_item::set_op(int op_num)
3101 if (op_num >= FIRST_OP) { // do we have an op value instead of an op number (index)?
3102 for (i=0; i<Num_operators; i++)
3103 if (op_num == Operators[i].value)
3104 op_num = i; // convert op value to op number
3108 text = Operators[op].text;
3109 type = (SEXPT_OPERATOR | SEXPT_VALID);
3112 // initialize node, type data
3113 // Defaults: t = SEXPT_STRING
3115 void sexp_list_item::set_data(char *str, int t)
3122 // add a node to end of list
3124 void sexp_list_item::add_op(int op_num)
3126 sexp_list_item *item, *ptr;
3128 item = new sexp_list_item;
3134 item->set_op(op_num);
3137 // add a node to end of list
3138 // Defaults: t = SEXPT_STRING
3140 void sexp_list_item::add_data(char *str, int t)
3142 sexp_list_item *item, *ptr;
3144 item = new sexp_list_item;
3150 item->set_data(str, t);
3153 // add a node to end of list, allocating memory for the text
3154 // Defaults: t = SEXPT_STRING
3156 void sexp_list_item::add_data_dup(char *str, int t)
3158 sexp_list_item *item, *ptr;
3160 item = new sexp_list_item;
3166 item->set_data(strdup(str), t);
3167 item->flags |= SEXP_ITEM_F_DUP;
3170 // add an sexp list to end of another list (join lists)
3172 void sexp_list_item::add_list(sexp_list_item *list)
3174 sexp_list_item *ptr;
3183 // free all nodes of list
3185 void sexp_list_item::destroy()
3187 sexp_list_item *ptr, *ptr2;
3192 if (ptr->flags & SEXP_ITEM_F_DUP)
3200 int sexp_tree::add_default_operator(int op, int argnum)
3204 sexp_list_item item;
3210 if (get_default_value(&item, op, argnum))
3213 if (item.type & SEXPT_OPERATOR) {
3214 SDL_assert((item.op >= 0) && (item.op < Num_operators));
3215 add_or_replace_operator(item.op);
3220 // special case for modify-variable (data added 1st arg is variable)
3221 if ( !stricmp(Operators[op].text, "modify-variable") ) {
3224 int sexp_var_index = get_index_sexp_variable_name(item.text);
3225 SDL_assert(sexp_var_index != -1);
3226 int type = SEXPT_VALID | SEXPT_VARIABLE;
3227 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
3228 type |= SEXPT_STRING;
3229 } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
3230 type |= SEXPT_NUMBER;
3235 char node_text[2*TOKEN_LENGTH + 2];
3236 sprintf(node_text, "%s(%s)", item.text, Sexp_variables[sexp_var_index].text);
3237 add_variable_data(node_text, type);
3239 // the the variable name
3241 SDL_assert(argnum == 1);
3242 sexp_list_item temp_item;
3243 temp_item.text = buf2;
3244 get_default_value(&temp_item, op, 0);
3245 int sexp_var_index = get_index_sexp_variable_name(temp_item.text);
3246 SDL_assert(sexp_var_index != -1);
3248 // from name get type
3249 int temp_type = Sexp_variables[sexp_var_index].type;
3251 if (temp_type & SEXP_VARIABLE_NUMBER) {
3252 type = SEXPT_VALID | SEXPT_NUMBER;
3253 } else if (temp_type & SEXP_VARIABLE_STRING) {
3254 type = SEXPT_VALID | SEXPT_STRING;
3258 add_data(item.text, type);
3261 add_data(item.text, item.type);
3268 int sexp_tree::get_default_value(sexp_list_item *item, int op, int i)
3272 sexp_list_item *list;
3277 type = query_operator_argument_type(op, i);
3280 item->set_op(OP_NOP);
3284 item->set_op(OP_TRUE);
3290 // if the top level operators is an AI goal, and we are adding the last number required,
3291 // assume that this number is a priority and make it 89 instead of 1.
3292 if ((query_operator_return_type(op) == OPR_AI_GOAL) && (i == (Operators[op].min - 1)))
3293 item->set_data("89", (SEXPT_NUMBER | SEXPT_VALID));
3294 else if (((Operators[op].value == OP_HAS_DOCKED_DELAY) || (Operators[op].value == OP_HAS_UNDOCKED_DELAY)) && (i == 2))
3295 item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
3296 else if ( (Operators[op].value == OP_SHIP_TYPE_DESTROYED) || (Operators[op].value == OP_GOOD_SECONDARY_TIME) )
3297 item->set_data("100", (SEXPT_NUMBER | SEXPT_VALID));
3299 item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
3304 list = get_listing_opf(type, index, i);
3311 strcpy(item->text, list->text);
3317 // catch anything that doesn't have a default value. Just describe what should be here instead
3320 case OPF_SHIP_NOT_PLAYER:
3322 case OPF_SHIP_POINT:
3323 case OPF_SHIP_WING_POINT:
3324 str = "<name of ship here>";
3328 str = "<name of wing here>";
3331 case OPF_DOCKER_POINT:
3332 str = "<docker point>";
3335 case OPF_DOCKEE_POINT:
3336 str = "<dockee point>";
3340 case OPF_AWACS_SUBSYSTEM:
3341 str = "<name of subsystem>";
3353 //str = "<any allied>";
3354 str = "<any wingman>";
3357 case OPF_WAYPOINT_PATH:
3358 str = "<waypoint path>";
3361 case OPF_MISSION_NAME:
3362 str = "<mission name>";
3366 str = "<goal name>";
3370 str = "<ship type here>";
3373 case OPF_EVENT_NAME:
3374 str = "<event name>";
3377 case OPF_HUGE_WEAPON:
3378 str = "<huge weapon type>";
3381 case OPF_JUMP_NODE_NAME:
3382 str = "<Jump node name>";
3386 str = "<new default required!>";
3390 item->set_data(str, (SEXPT_STRING | SEXPT_VALID));
3394 int sexp_tree::query_default_argument_available(int op)
3398 SDL_assert(op >= 0);
3399 for (i=0; i<Operators[op].min; i++)
3400 if (!query_default_argument_available(op, i))
3406 int sexp_tree::query_default_argument_available(int op, int i)
3411 type = query_operator_argument_type(op, i);
3422 case OPF_AWACS_SUBSYSTEM:
3423 case OPF_DOCKER_POINT:
3424 case OPF_DOCKEE_POINT:
3428 case OPF_SKILL_LEVEL:
3429 case OPF_MEDAL_NAME:
3430 case OPF_WEAPON_NAME:
3431 case OPF_SHIP_CLASS_NAME:
3432 case OPF_HUD_GAUGE_NAME:
3433 case OPF_HUGE_WEAPON:
3434 case OPF_JUMP_NODE_NAME:
3439 case OPF_SHIP_NOT_PLAYER:
3441 case OPF_SHIP_POINT:
3442 case OPF_SHIP_WING_POINT:
3443 ptr = GET_FIRST(&obj_used_list);
3444 while (ptr != END_OF_LIST(&obj_used_list)) {
3445 if (ptr->type == OBJ_SHIP)
3448 ptr = GET_NEXT(ptr);
3454 for (j=0; j<MAX_WINGS; j++)
3455 if (Wings[j].wave_count)
3461 case OPF_WAYPOINT_PATH:
3462 if (Num_waypoint_lists)
3467 case OPF_MISSION_NAME:
3468 if (m_mode != MODE_CAMPAIGN) {
3469 if (!(*Mission_filename))
3475 if (Campaign.num_missions > 0)
3480 case OPF_GOAL_NAME: {
3483 value = Operators[op].value;
3485 if (m_mode == MODE_CAMPAIGN)
3488 // need to be sure that previous-goal functions are available. (i.e. we are providing a default argument for them)
3489 else if ((value == OP_PREVIOUS_GOAL_TRUE) || (value == OP_PREVIOUS_GOAL_FALSE) || (value == OP_PREVIOUS_GOAL_INCOMPLETE) || Num_goals)
3495 case OPF_EVENT_NAME: {
3498 value = Operators[op].value;
3499 if (m_mode == MODE_CAMPAIGN)
3502 // need to be sure that previous-event functions are available. (i.e. we are providing a default argument for them)
3503 else if ((value == OP_PREVIOUS_EVENT_TRUE) || (value == OP_PREVIOUS_EVENT_FALSE) || (value == OP_PREVIOUS_EVENT_INCOMPLETE) || Num_mission_events)
3510 if (m_mode == MODE_EVENTS) {
3511 SDL_assert(Event_editor_dlg);
3512 if (Event_editor_dlg->current_message_name(0))
3516 if (Num_messages > Num_builtin_messages)
3522 case OPF_VARIABLE_NAME:
3523 if (sexp_variable_count() > 0) {
3537 // expand a combined line (one with an operator and it's one argument on the same line) into
3539 void sexp_tree::expand_operator(int node)
3544 if (nodes[node].flags & COMBINED) {
3545 node = nodes[node].parent;
3546 SDL_assert((nodes[node].flags & OPERAND) && (nodes[node].flags & EDITABLE));
3549 if ((nodes[node].flags & OPERAND) && (nodes[node].flags & EDITABLE)) { // expandable?
3550 SDL_assert(nodes[node].type & SEXPT_OPERATOR);
3551 h = nodes[node].handle;
3552 data = nodes[node].child;
3553 SDL_assert(data != -1 && nodes[data].next == -1 && nodes[data].child == -1);
3555 SetItem(h, TVIF_TEXT, nodes[node].text, 0, 0, 0, 0, 0);
3556 nodes[node].flags = OPERAND;
3557 nodes[data].handle = insert(nodes[data].text, BITMAP_DATA, BITMAP_DATA, h);
3558 nodes[data].flags = EDITABLE;
3559 Expand(h, TVE_EXPAND);
3563 // expand a CTreeCtrl branch and all of it's children
3564 void sexp_tree::expand_branch(HTREEITEM h)
3566 Expand(h, TVE_EXPAND);
3567 h = GetChildItem(h);
3570 h = GetNextSiblingItem(h);
3574 void sexp_tree::merge_operator(int node)
3579 if (nodes[node].flags == EDITABLE) // data
3580 node = nodes[node].parent;
3583 child = nodes[node].child;
3584 if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) {
3585 sprintf(buf, "%s %s", nodes[node].text, nodes[child].text);
3586 SetItemText(nodes[node].handle, buf);
3587 nodes[node].flags = OPERAND | EDITABLE;
3588 nodes[child].flags = COMBINED;
3589 DeleteItem(nodes[child].handle);
3590 nodes[child].handle = NULL;
3596 // add a data node under operator pointed to by item_index
3597 int sexp_tree::add_data(char *data, int type)
3601 expand_operator(item_index);
3602 node = allocate_node(item_index);
3603 set_node(node, type, data);
3604 nodes[node].handle = insert(data, BITMAP_DATA, BITMAP_DATA, nodes[item_index].handle);
3605 nodes[node].flags = EDITABLE;
3610 // add a (variable) data node under operator pointed to by item_index
3611 int sexp_tree::add_variable_data(char *data, int type)
3615 SDL_assert(type & SEXPT_VARIABLE);
3617 expand_operator(item_index);
3618 node = allocate_node(item_index);
3619 set_node(node, type, data);
3620 nodes[node].handle = insert(data, BITMAP_VARIABLE, BITMAP_VARIABLE, nodes[item_index].handle);
3621 nodes[node].flags = NOT_EDITABLE;
3626 // add an operator under operator pointed to by item_index. Updates item_index to point
3627 // to this new operator.
3628 void sexp_tree::add_operator(char *op, HTREEITEM h)
3632 if (item_index == -1) {
3633 node = allocate_node(-1);
3634 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
3635 item_handle = nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, h);
3638 expand_operator(item_index);
3639 node = allocate_node(item_index);
3640 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
3641 item_handle = nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, nodes[item_index].handle);
3644 nodes[node].flags = OPERAND;
3649 // add an operator with one argument under operator pointed to by item_index. This function
3650 // exists because the one arg case is a special case. The operator and argument is
3651 // displayed on the same line.
3652 /*void sexp_tree::add_one_arg_operator(char *op, char *data, int type)
3657 expand_operator(item_index);
3658 node1 = allocate_node(item_index);
3659 node2 = allocate_node(node1);
3660 set_node(node1, SEXPT_OPERATOR, op);
3661 set_node(node2, type, data);
3662 sprintf(str, "%s %s", op, data);
3663 nodes[node1].handle = insert(str, nodes[item_index].handle);
3664 nodes[node1].flags = OPERAND | EDITABLE;
3665 nodes[node2].flags = COMBINED;
3670 int sexp_tree::verify_tree(int *bypass)
3672 return verify_tree(0, bypass);
3675 // check the sexp tree for errors. Return -1 if error, or 0 if no errors. If an error
3676 // is found, item_index = node of error.
3677 int sexp_tree::verify_tree(int node, int *bypass)
3679 int i, type, count, op, type2, op2, argnum = 0;
3682 return 0; // nothing to check
3684 SDL_assert(node >= 0 && node < MAX_SEXP_TREE_SIZE);
3685 SDL_assert(nodes[node].type == SEXPT_OPERATOR);
3687 op = identify_operator(nodes[node].text);
3689 return node_error(node, "Unknown operator", bypass);
3691 count = count_args(nodes[node].child);
3692 if (count < Operators[op].min)
3693 return node_error(node, "Too few arguments for operator", bypass);
3694 if (count > Operators[op].max)
3695 return node_error(node, "Too many arguments for operator", bypass);
3697 node = nodes[node].child; // get first argument
3698 while (node != -1) {
3699 type = query_operator_argument_type(op, argnum);
3700 SDL_assert(nodes[node].type & SEXPT_VALID);
3701 if (nodes[node].type == SEXPT_OPERATOR) {
3702 if (verify_tree(node) == -1)
3705 op2 = identify_operator(nodes[node].text); // no error checking, because it was done in the call above.
3706 type2 = query_operator_return_type(op2);
3708 } else if (nodes[node].type == SEXPT_NUMBER) {
3712 ptr = nodes[node].text;
3714 if (!isdigit(*ptr++))
3715 return node_error(node, "Number is invalid", bypass);
3717 } else if (nodes[node].type == SEXPT_STRING) {
3718 type2 = SEXP_ATOM_STRING;
3721 SDL_assert(0); // unknown and invalid sexp node type.
3725 if (type2 != OPR_NUMBER)
3726 return node_error(node, "Number or number return type expected here", bypass);
3731 if (type2 == SEXP_ATOM_STRING)
3732 if (ship_name_lookup(nodes[node].text) == -1)
3735 if (type2 != SEXP_ATOM_STRING)
3736 return node_error(node, "Ship name expected here", bypass);
3741 if (type2 == SEXP_ATOM_STRING)
3742 if (wing_name_lookup(nodes[node].text) == -1)
3745 if (type2 != SEXP_ATOM_STRING)
3746 return node_error(node, "Wing name expected here", bypass);
3751 if (type2 == SEXP_ATOM_STRING)
3752 if (ship_name_lookup(nodes[node].text) == -1)
3753 if (wing_name_lookup(nodes[node].text) == -1)
3756 if (type2 != SEXP_ATOM_STRING)
3757 return node_error(node, "Ship or wing name expected here", bypass);
3762 if (type2 != OPR_BOOL)
3763 return node_error(node, "Boolean return type expected here", bypass);
3768 if (type2 != OPR_NULL)
3769 return node_error(node, "No return type operator expected here", bypass);
3774 if (type2 != SEXP_ATOM_STRING || verify_vector(nodes[node].text))
3775 return node_error(node, "3d coordinate expected here", bypass);
3780 if (type2 == SEXP_ATOM_STRING)
3781 if (ai_get_subsystem_type(nodes[node].text) == SUBSYSTEM_UNKNOWN)
3784 if (type2 != SEXP_ATOM_STRING)
3785 return node_error(node, "Subsystem name expected here", bypass);
3790 if (type2 == SEXP_ATOM_STRING) {
3791 for (i=0; i<Num_team_names; i++)
3792 if (!stricmp(Team_names[i], nodes[node].text))
3796 if (i == Num_team_names)
3797 return node_error(node, "Iff team type expected here", bypass);
3802 if (type2 != OPR_AI_GOAL)
3803 return node_error(node, "Ai goal return type expected here", bypass);
3807 case OPF_DOCKER_POINT:
3808 if (type2 != SEXP_ATOM_STRING)
3809 return node_error(node, "Docker docking point name expected here", bypass);
3813 case OPF_DOCKEE_POINT:
3814 if (type2 != SEXP_ATOM_STRING)
3815 return node_error(node, "Dockee docking point name expected here", bypass);
3820 node = nodes[node].next;
3828 // display an error message and position to point of error (a node)
3829 int sexp_tree::node_error(int node, char *msg, int *bypass)
3837 item_handle = nodes[node].handle;
3838 if (nodes[node].flags & COMBINED)
3839 item_handle = nodes[nodes[node].parent].handle;
3841 ensure_visible(node);
3842 SelectItem(item_handle);
3843 sprintf(text, "%s\n\nContinue checking for more errors?", msg);
3844 if (MessageBox(text, "Sexp error", MB_YESNO | MB_ICONEXCLAMATION) == IDNO)
3850 void sexp_tree::hilite_item(int node)
3852 ensure_visible(node);
3853 SelectItem(nodes[node].handle);
3856 // because the MFC function EnsureVisible() doesn't do what it says it does, I wrote this.
3857 void sexp_tree::ensure_visible(int node)
3859 SDL_assert(node != -1);
3860 if (nodes[node].parent != -1)
3861 ensure_visible(nodes[node].parent); // expand all parents first
3863 if (nodes[node].child != -1) // expandable?
3864 Expand(nodes[node].handle, TVE_EXPAND); // expand this item
3867 void sexp_tree::link_modified(int *ptr)
3872 void get_variable_default_text_from_variable_text(char *text, char *default_text)
3878 start = strstr(text, "(");
3882 // get length and copy all but last char ")"
3883 len = strlen(start);
3884 strncpy(default_text, start, len-1);
3886 // add null termination
3887 default_text[len-1] = '\0';
3890 void get_variable_name_from_sexp_tree_node_text(const char *text, char *var_name)
3893 length = strcspn(text, "(");
3895 strncpy(var_name, text, length);
3896 var_name[length] = '\0';
3899 int sexp_tree::get_modify_variable_type()
3901 SDL_assert(item_index > -1);
3905 int parent = nodes[item_index].parent;
3906 SDL_assert(parent != -1);
3908 if ( !stricmp(nodes[parent].text, "modify-variable") ) {
3909 SDL_assert(nodes[parent].child != -1);
3910 sexp_var_index = get_tree_name_to_sexp_variable_index(nodes[nodes[parent].child].text);
3911 SDL_assert(sexp_var_index != -1);
3913 Int3(); // should not be called otherwise
3916 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
3918 } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
3919 return OPF_AMBIGUOUS;
3927 void sexp_tree::verify_and_fix_arguments(int node)
3929 int op, arg_num, type, tmp;
3930 static int flag = 0;
3931 sexp_list_item *list, *ptr;
3937 op = identify_operator(nodes[node].text);
3945 item_index = nodes[node].child;
3946 while (item_index >= 0) {
3947 // get listing of valid argument values for node item_index
3948 type = query_operator_argument_type(op, arg_num);
3949 // special case for modify-variable
3950 if (type == OPF_AMBIGUOUS) {
3951 // check if parent variable type is number, returns OPF_NUMBER or OPF_AMBIGUOUS
3952 type = get_modify_variable_type();
3954 if (query_restricted_opf_range(type)) {
3955 list = get_listing_opf(type, node, arg_num);
3956 if (!list && (arg_num >= Operators[op].min)) {
3957 free_node(item_index, 1);
3964 // get a pointer to nodes[item_index].text for normal value
3965 // or default variable value if variable
3967 char default_variable_text[TOKEN_LENGTH];
3968 if (nodes[item_index].type & SEXPT_VARIABLE) {
3969 // special case for modify-variable
3970 if ( !stricmp(Operators[op].text, "modify-variable") ) {
3971 // make text_ptr to start - before '('
3972 get_variable_name_from_sexp_tree_node_text(nodes[item_index].text, default_variable_text);
3973 text_ptr = default_variable_text;
3975 get_variable_default_text_from_variable_text(nodes[item_index].text, default_variable_text);
3976 text_ptr = default_variable_text;
3979 text_ptr = nodes[item_index].text;
3985 if (ptr->text != NULL) {
3986 // make sure text is not NULL
3987 // check that proposed text is valid for operator
3988 if ( !stricmp(ptr->text, text_ptr) )
3993 // text is NULL, so set ptr to NULL to end loop
3998 if (!ptr) { // argument isn't in list of valid choices,
3999 if (list->op >= 0) {
4000 replace_operator(list->text);
4002 replace_data(list->text, list->type);
4007 bool invalid = false;
4008 if (type == OPF_AMBIGUOUS) {
4009 if (SEXPT_TYPE(nodes[item_index].type) == SEXPT_OPERATOR) {
4013 if (SEXPT_TYPE(nodes[item_index].type) != SEXPT_OPERATOR) {
4019 replace_data("<Invalid>", (SEXPT_STRING | SEXPT_VALID));
4023 if (nodes[item_index].type & SEXPT_OPERATOR)
4024 verify_and_fix_arguments(item_index);
4027 item_index = nodes[item_index].next;
4035 void sexp_tree::replace_data(char *data, int type)
4040 node = nodes[item_index].child;
4044 nodes[item_index].child = -1;
4045 h = nodes[item_index].handle;
4046 while (ItemHasChildren(h))
4047 DeleteItem(GetChildItem(h));
4049 set_node(item_index, type, data);
4050 SetItemText(h, data);
4051 SetItemImage(h, BITMAP_DATA, BITMAP_DATA);
4052 nodes[item_index].flags = EDITABLE;
4054 // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
4055 verify_and_fix_arguments(nodes[item_index].parent);
4058 update_help(GetSelectedItem());
4062 // Replaces data with sexp_variable type data
4063 void sexp_tree::replace_variable_data(int var_idx, int type)
4069 SDL_assert(type & SEXPT_VARIABLE);
4071 node = nodes[item_index].child;
4075 nodes[item_index].child = -1;
4076 h = nodes[item_index].handle;
4077 while (ItemHasChildren(h)) {
4078 DeleteItem(GetChildItem(h));
4082 sprintf(buf, "%s(%s)", Sexp_variables[var_idx].variable_name, Sexp_variables[var_idx].text);
4084 set_node(item_index, type, buf);
4085 SetItemText(h, buf);
4086 SetItemImage(h, BITMAP_VARIABLE, BITMAP_VARIABLE);
4087 nodes[item_index].flags = NOT_EDITABLE;
4089 // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
4090 verify_and_fix_arguments(nodes[item_index].parent);
4093 update_help(GetSelectedItem());
4098 void sexp_tree::replace_operator(char *op)
4103 node = nodes[item_index].child;
4107 nodes[item_index].child = -1;
4108 h = nodes[item_index].handle;
4109 while (ItemHasChildren(h))
4110 DeleteItem(GetChildItem(h));
4112 set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), op);
4114 nodes[item_index].flags = OPERAND;
4116 update_help(GetSelectedItem());
4118 // hack added at Allender's request. If changing ship in an ai-dock operator, re-default
4122 /*void sexp_tree::replace_one_arg_operator(char *op, char *data, int type)
4128 node = nodes[item_index].child;
4132 nodes[item_index].child = -1;
4133 h = nodes[item_index].handle;
4134 while (ItemHasChildren(h))
4135 DeleteItem(GetChildItem(h));
4137 node = allocate_node(item_index);
4138 set_node(item_index, SEXPT_OPERATOR, op);
4139 set_node(node, type, data);
4140 sprintf(str, "%s %s", op, data);
4141 SetItemText(h, str);
4142 nodes[item_index].flags = OPERAND | EDITABLE;
4143 nodes[node].flags = COMBINED;
4145 update_help(GetSelectedItem());
4148 // moves a whole sexp tree branch to a new position under 'parent' and after 'after'.
4149 // The expansion state is preserved, and node handles are updated.
4150 void sexp_tree::move_branch(int source, int parent)
4154 // if no source, skip everything
4156 node = nodes[source].parent;
4158 if (nodes[node].child == source)
4159 nodes[node].child = nodes[source].next;
4161 node = nodes[node].child;
4162 while (nodes[node].next != source) {
4163 node = nodes[node].next;
4164 SDL_assert(node != -1);
4167 nodes[node].next = nodes[source].next;
4171 nodes[source].parent = parent;
4172 nodes[source].next = -1;
4174 if (nodes[parent].child == -1)
4175 nodes[parent].child = source;
4177 node = nodes[parent].child;
4178 while (nodes[node].next != -1)
4179 node = nodes[node].next;
4181 nodes[node].next = source;
4184 move_branch(nodes[source].handle, nodes[parent].handle);
4187 move_branch(nodes[source].handle);
4191 HTREEITEM sexp_tree::move_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after)
4193 int i, image1, image2;
4194 HTREEITEM h = 0, child, next;
4197 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4198 if (nodes[i].handle == source)
4201 if (i < MAX_SEXP_TREE_SIZE) {
4202 GetItemImage(source, image1, image2);
4203 h = insert(GetItemText(source), image1, image2, parent, after);
4204 nodes[i].handle = h;
4207 GetItemImage(source, image1, image2);
4208 h = insert(GetItemText(source), image1, image2, parent, after);
4211 SetItemData(h, GetItemData(source));
4212 child = GetChildItem(source);
4214 next = GetNextSiblingItem(child);
4215 move_branch(child, h);
4219 if (GetItemState(source, TVIS_EXPANDED) & TVIS_EXPANDED)
4220 Expand(h, TVE_EXPAND);
4228 void sexp_tree::copy_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after)
4230 int i, image1, image2;
4234 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4235 if (nodes[i].handle == source)
4238 if (i < MAX_SEXP_TREE_SIZE) {
4239 GetItemImage(source, image1, image2);
4240 h = insert(GetItemText(source), image1, image2, parent, after);
4241 nodes[i].handle = h;
4244 GetItemImage(source, image1, image2);
4245 h = insert(GetItemText(source), image1, image2, parent, after);
4248 SetItemData(h, GetItemData(source));
4249 child = GetChildItem(source);
4251 copy_branch(child, h);
4252 child = GetNextSiblingItem(child);
4255 if (GetItemState(source, TVIS_EXPANDED) & TVIS_EXPANDED)
4256 Expand(h, TVE_EXPAND);
4260 void sexp_tree::swap_roots(HTREEITEM one, HTREEITEM two)
4264 SDL_assert(!GetParentItem(one));
4265 SDL_assert(!GetParentItem(two));
4266 // copy_branch(one, TVI_ROOT, two);
4267 // move_branch(two, TVI_ROOT, one);
4269 h = move_branch(one, TVI_ROOT, two);
4275 void sexp_tree::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
4279 // ScreenToClient(&m_pt);
4280 ASSERT(!m_dragging);
4281 m_h_drag = HitTest(m_pt, &flags);
4284 if (!m_mode || GetParentItem(m_h_drag))
4287 ASSERT(m_p_image_list == NULL);
4288 m_p_image_list = CreateDragImage(m_h_drag); // get the image list for dragging
4289 if (!m_p_image_list)
4292 m_p_image_list->DragShowNolock(TRUE);
4293 m_p_image_list->SetDragCursorImage(0, CPoint(0, 0));
4294 m_p_image_list->BeginDrag(0, CPoint(0,0));
4295 m_p_image_list->DragMove(m_pt);
4296 m_p_image_list->DragEnter(this, m_pt);
4301 void sexp_tree::OnLButtonDown(UINT nFlags, CPoint point)
4304 CTreeCtrl::OnLButtonDown(nFlags, point);
4307 void sexp_tree::OnMouseMove(UINT nFlags, CPoint point)
4313 ASSERT(m_p_image_list != NULL);
4314 m_p_image_list->DragMove(point);
4315 if ((hitem = HitTest(point, &flags)) != NULL)
4316 if (!GetParentItem(hitem)) {
4317 m_p_image_list->DragLeave(this);
4318 SelectDropTarget(hitem);
4320 m_p_image_list->DragEnter(this, point);
4324 CTreeCtrl::OnMouseMove(nFlags, point);
4327 void sexp_tree::OnLButtonUp(UINT nFlags, CPoint point)
4332 ASSERT(m_p_image_list != NULL);
4333 m_p_image_list->DragLeave(this);
4334 m_p_image_list->EndDrag();
4335 delete m_p_image_list;
4336 m_p_image_list = NULL;
4338 if (m_h_drop && m_h_drag != m_h_drop) {
4339 SDL_assert(m_h_drag);
4340 index1 = GetItemData(m_h_drag);
4341 index2 = GetItemData(m_h_drop);
4342 swap_roots(m_h_drag, m_h_drop);
4343 if (m_mode == MODE_GOALS) {
4344 SDL_assert(Goal_editor_dlg);
4345 Goal_editor_dlg->swap_handler(index1, index2);
4347 } else if (m_mode == MODE_EVENTS) {
4348 SDL_assert(Event_editor_dlg);
4349 Event_editor_dlg->swap_handler(index1, index2);
4351 } else if (m_mode == MODE_CAMPAIGN) {
4352 Campaign_tree_formp->swap_handler(index1, index2);
4362 SelectDropTarget(NULL);
4365 CTreeCtrl::OnLButtonUp(nFlags, point);
4368 void sexp_tree::setup(CEdit *ptr)
4370 CImageList *pimagelist;
4375 int stops[2] = { 10, 30 };
4377 help_box -> SetTabStops(2, (LPINT) stops);
4380 pimagelist = GetImageList(TVSIL_NORMAL);
4382 pimagelist = new CImageList();
4383 pimagelist->Create(16, 16, TRUE/*bMask*/, 2, 9);
4385 bitmap.LoadBitmap(IDB_OPERATOR);
4386 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4387 bitmap.DeleteObject();
4389 bitmap.LoadBitmap(IDB_DATA);
4390 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4391 bitmap.DeleteObject();
4393 bitmap.LoadBitmap(IDB_VARIABLE);
4394 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4395 bitmap.DeleteObject();
4397 bitmap.LoadBitmap(IDB_ROOT);
4398 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4399 bitmap.DeleteObject();
4401 bitmap.LoadBitmap(IDB_ROOT_DIRECTIVE);
4402 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4403 bitmap.DeleteObject();
4405 bitmap.LoadBitmap(IDB_CHAINED);
4406 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4407 bitmap.DeleteObject();
4409 bitmap.LoadBitmap(IDB_CHAINED_DIRECTIVE);
4410 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4411 bitmap.DeleteObject();
4413 bitmap.LoadBitmap(IDB_GREEN_DOT);
4414 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4415 bitmap.DeleteObject();
4417 bitmap.LoadBitmap(IDB_BLACK_DOT);
4418 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4419 bitmap.DeleteObject();
4421 SetImageList(pimagelist, TVSIL_NORMAL);
4424 //#define BITMAP_OPERATOR 0
4425 //#define BITMAP_DATA 1
4426 //#define BITMAP_VARIABLE 2
4427 //#define BITMAP_ROOT 3
4428 //#define BITMAP_ROOT_DIRECTIVE 4
4429 //#define BITMAP_CHAIN 5
4430 //#define BITMAP_CHAIN_DIRECTIVE 6
4431 //#define BITMAP_GREEN_DOT 7
4432 //#define BITMAP_BLACK_DOT 8
4435 HTREEITEM sexp_tree::insert(LPCTSTR lpszItem, int image, int sel_image, HTREEITEM hParent, HTREEITEM hInsertAfter)
4437 return InsertItem(lpszItem, image, sel_image, hParent, hInsertAfter);
4441 void sexp_tree::OnDestroy()
4443 CImageList *pimagelist;
4445 pimagelist = GetImageList(TVSIL_NORMAL);
4447 pimagelist->DeleteImageList();
4451 CTreeCtrl::OnDestroy();
4454 HTREEITEM sexp_tree::handle(int node)
4456 return nodes[node].handle;
4459 char *sexp_tree::help(int code)
4463 i = sizeof(Sexp_help) / sizeof(sexp_help_struct);
4465 if (Sexp_help[i].id == code)
4470 return Sexp_help[i].help;
4475 // get type of item clicked on
4476 int sexp_tree::get_type(HTREEITEM h)
4480 // get index into sexp_tree
4481 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4482 if (nodes[i].handle == h)
4485 if ( (i >= MAX_SEXP_TREE_SIZE) ) {
4486 // Int3(); // This would be the root of the tree -- ie, event name
4490 return nodes[i].type;
4494 void sexp_tree::update_help(HTREEITEM h)
4497 int i, j, z, c, code;
4500 for (i=0; i<Num_operators; i++)
4501 for (j=0; j<Num_op_menus; j++)
4502 if ((Operators[i].value & OP_CATAGORY_MASK) == op_menu[j].id) {
4503 if (!help(Operators[i].value))
4504 Int3(); // Damn you, Allender! If you add new sexp operators, add help for them too! :)
4507 help_box = (CEdit *) GetParent()->GetDlgItem(IDC_HELP_BOX);
4508 if (!help_box || !::IsWindow(help_box->m_hWnd))
4511 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4512 if (nodes[i].handle == h)
4515 if ((i >= MAX_SEXP_TREE_SIZE) || !nodes[i].type) {
4516 help_box->SetWindowText("");
4520 if (SEXPT_TYPE(nodes[i].type) != SEXPT_OPERATOR) {
4521 z = nodes[i].parent;
4523 Warning(LOCATION, "Sexp data \"%s\" has no parent!", nodes[i].text);
4527 code = identify_operator(nodes[z].text);
4531 while ((j >= 0) && (j != i)) {
4537 if (query_operator_argument_type(code, c) == OPF_MESSAGE) {
4538 for (j=0; j<Num_messages; j++)
4539 if (!stricmp(Messages[j].name, nodes[i].text)) {
4540 text.Format("Message Text:\r\n%s", Messages[j].message);
4541 help_box->SetWindowText(text);
4550 code = find_operator(nodes[i].text);
4553 str = "No help available";
4555 help_box->SetWindowText(str);
4558 // find list of sexp_tree nodes with text
4559 // stuff node indices into find[]
4560 int sexp_tree::find_text(char *text, int *find)
4565 for (i=0; i<MAX_SEARCH_MESSAGE_DEPTH; i++) {
4571 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
4572 // only look at used and editable nodes
4573 if ((nodes[i].flags & EDITABLE && (nodes[i].type != SEXPT_UNUSED))) {
4575 if ( !stricmp(nodes[i].text, text) ) {
4576 find[find_count++] = i;
4578 // don't exceed max count - array bounds
4579 if (find_count == MAX_SEARCH_MESSAGE_DEPTH) {
4590 void sexp_tree::OnKeydown(NMHDR *pNMHDR, LRESULT *pResult)
4593 TV_KEYDOWN *pTVKeyDown = (TV_KEYDOWN *) pNMHDR;
4595 key = pTVKeyDown->wVKey;
4596 if (key == VK_SPACE)
4597 EditLabel(GetSelectedItem());
4602 // Determine if a given opf code has a restricted argument range (i.e. has a specific, limited
4603 // set of argument values, or has virtually unlimited possibilities. For example, boolean values
4604 // only have true or false, so it is restricted, but a number could be anything, so it's not.
4606 int sexp_tree::query_restricted_opf_range(int opf)
4618 // generate listing of valid argument values.
4619 // opf = operator format to generate list for
4620 // parent_node = the parent node we are generating list for
4621 // arg_index = argument number of parent this argument will go at
4623 sexp_list_item *sexp_tree::get_listing_opf(int opf, int parent_node, int arg_index)
4630 return get_listing_opf_null();
4633 return get_listing_opf_bool(parent_node);
4636 return get_listing_opf_number();
4639 return get_listing_opf_ship(parent_node);
4642 return get_listing_opf_wing();
4644 case OPF_AWACS_SUBSYSTEM:
4646 return get_listing_opf_subsystem(parent_node, arg_index);
4649 return get_listing_opf_point();
4652 return get_listing_opf_iff();
4655 return get_listing_opf_ai_goal(parent_node);
4657 case OPF_DOCKER_POINT:
4658 return get_listing_opf_docker_point(parent_node);
4660 case OPF_DOCKEE_POINT:
4661 return get_listing_opf_dockee_point(parent_node);
4664 return get_listing_opf_message();
4667 return get_listing_opf_who_from();
4670 return get_listing_opf_priority();
4672 case OPF_WAYPOINT_PATH:
4673 return get_listing_opf_waypoint_path();
4676 return get_listing_opf_positive();
4678 case OPF_MISSION_NAME:
4679 return get_listing_opf_mission_name();
4681 case OPF_SHIP_POINT:
4682 return get_listing_opf_ship_point();
4685 return get_listing_opf_goal_name(parent_node);
4688 return get_listing_opf_ship_wing();
4690 case OPF_SHIP_WING_POINT:
4691 return get_listing_opf_ship_wing_point();
4694 return get_listing_opf_ship_type();
4697 return get_listing_opf_keypress();
4699 case OPF_EVENT_NAME:
4700 return get_listing_opf_event_name(parent_node);
4703 return get_listing_opf_ai_order();
4705 case OPF_SKILL_LEVEL:
4706 return get_listing_opf_skill_level();
4708 case OPF_MEDAL_NAME:
4709 return get_listing_opf_medal_name();
4711 case OPF_WEAPON_NAME:
4712 return get_listing_opf_weapon_name();
4714 case OPF_SHIP_CLASS_NAME:
4715 return get_listing_opf_ship_class_name();
4717 case OPF_HUD_GAUGE_NAME:
4718 return get_listing_opf_hud_gauge_name();
4720 case OPF_HUGE_WEAPON:
4721 return get_listing_opf_huge_weapon();
4723 case OPF_SHIP_NOT_PLAYER:
4724 return get_listing_opf_ship_not_player();
4726 case OPF_JUMP_NODE_NAME:
4727 return get_listing_opf_jump_nodes();
4729 case OPF_VARIABLE_NAME:
4730 return get_listing_opf_variable_names();
4736 Int3(); // unknown OPF code
4742 sexp_list_item *sexp_tree::get_listing_opf_null()
4745 sexp_list_item head;
4747 for (i=0; i<Num_operators; i++)
4748 if (query_operator_return_type(i) == OPR_NULL)
4754 sexp_list_item *sexp_tree::get_listing_opf_bool(int parent_node)
4757 sexp_list_item head;
4759 // search for the previous goal/event operators. If found, only add the true/false
4760 // sexpressions to the list
4762 if ( parent_node != -1 ) {
4765 op = find_operator(nodes[parent_node].text);
4766 if ( (op == OP_PREVIOUS_GOAL_TRUE) || (op == OP_PREVIOUS_GOAL_FALSE) || (op == OP_PREVIOUS_EVENT_TRUE) || (op == OP_PREVIOUS_EVENT_FALSE) )
4771 for (i=0; i<Num_operators; i++) {
4772 if (query_operator_return_type(i) == OPR_BOOL) {
4773 if ( !only_basic || (only_basic && ((Operators[i].value == OP_TRUE) || (Operators[i].value == OP_FALSE))) ) {
4782 sexp_list_item *sexp_tree::get_listing_opf_positive()
4785 sexp_list_item head;
4787 for (i=0; i<Num_operators; i++)
4788 if (query_operator_return_type(i) == OPR_POSITIVE)
4794 sexp_list_item *sexp_tree::get_listing_opf_number()
4797 sexp_list_item head;
4799 for (i=0; i<Num_operators; i++) {
4800 z = query_operator_return_type(i);
4801 if ((z == OPR_NUMBER) || (z == OPR_POSITIVE))
4808 sexp_list_item *sexp_tree::get_listing_opf_ship(int parent_node)
4811 sexp_list_item head;
4812 int op = 0, dock_ship = -1, require_cap_ship = 0;
4814 // look at the parent node and get the operator. Some ship lists should be filtered based
4815 // on what the parent operator is
4816 if ( parent_node >= 0 ) {
4817 op = find_operator(nodes[parent_node].text);
4819 // prune out to only capital ships
4820 if (!stricmp(nodes[parent_node].text, "cap-subsys-cargo-known-delay")) {
4821 require_cap_ship = 1;
4824 // get the dock_ship number of if this goal is an ai dock goal. used to prune out unwanted ships out
4825 // of the generated ship list
4827 if ( op == OP_AI_DOCK ) {
4830 z = nodes[parent_node].parent;
4832 SDL_assert(!stricmp(nodes[z].text, "add-ship-goal") || !stricmp(nodes[z].text, "add-wing-goal") || !stricmp(nodes[z].text, "add-goal"));
4837 dock_ship = ship_name_lookup(nodes[z].text, 1);
4838 SDL_assert( dock_ship != -1 );
4842 ptr = GET_FIRST(&obj_used_list);
4843 while (ptr != END_OF_LIST(&obj_used_list)) {
4844 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
4845 if ( op == OP_AI_DOCK ) {
4846 // only include those ships in the list which the given ship can dock with.
4847 if ( (dock_ship != ptr->instance) && ship_docking_valid(dock_ship , ptr->instance) )
4848 head.add_data(Ships[ptr->instance].ship_name );
4851 if ( !require_cap_ship || (Ship_info[Ships[ptr->instance].ship_info_index].flags & SIF_HUGE_SHIP) ) {
4852 head.add_data(Ships[ptr->instance].ship_name);
4857 ptr = GET_NEXT(ptr);
4863 sexp_list_item *sexp_tree::get_listing_opf_wing()
4866 sexp_list_item head;
4868 for (i=0; i<MAX_WINGS; i++){
4869 if (Wings[i].wave_count){
4870 head.add_data(Wings[i].name);
4877 // specific types of subsystems we're looking for
4878 #define OPS_CAP_CARGO 1
4879 #define OPS_STRENGTH 2
4880 #define OPS_BEAM_TURRET 3
4882 sexp_list_item *sexp_tree::get_listing_opf_subsystem(int parent_node, int arg_index)
4885 int special_subsys = 0;
4886 sexp_list_item head;
4887 ship_subsys *subsys;
4889 // determine if the parent is one of the set subsystem strength items. If so,
4890 // we want to append the "Hull" name onto the end of the menu
4891 SDL_assert(parent_node >= 0);
4893 // get the operator type of the node
4894 op = find_operator(nodes[parent_node].text);
4897 child = nodes[parent_node].child;
4898 SDL_assert(child >= 0);
4901 // where we care about hull strength
4902 case OP_REPAIR_SUBSYSTEM:
4903 case OP_SABOTAGE_SUBSYSTEM:
4904 case OP_SET_SUBSYSTEM_STRNGTH:
4905 special_subsys = OPS_STRENGTH;
4909 case OP_AWACS_SET_RADIUS:
4910 special_subsys = OPS_AWACS;
4913 // where we care about capital ship subsystem cargo
4914 case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
4915 special_subsys = OPS_CAP_CARGO;
4917 // get the next sibling
4918 child = nodes[child].next;
4921 // where we care about turrets carrying beam weapons
4923 special_subsys = OPS_BEAM_TURRET;
4925 // if this is arg index 3 (targeted ship)
4927 SDL_assert(arg_index == 3);
4928 child = nodes[child].next;
4929 SDL_assert(child >= 0);
4930 child = nodes[child].next;
4932 SDL_assert(arg_index == 1);
4937 // now find the ship and add all relevant subsystems
4938 SDL_assert(child >= 0);
4939 sh = ship_name_lookup(nodes[child].text, 1);
4941 subsys = GET_FIRST(&Ships[sh].subsys_list);
4942 while (subsys != END_OF_LIST(&Ships[sh].subsys_list)) {
4944 switch(special_subsys){
4947 if (valid_cap_subsys_cargo_list(subsys->system_info->subobj_name) ) {
4948 head.add_data(subsys->system_info->subobj_name);
4953 case OPS_BEAM_TURRET:
4954 head.add_data(subsys->system_info->subobj_name);
4959 if (subsys->system_info->flags & MSS_FLAG_AWACS) {
4960 head.add_data(subsys->system_info->subobj_name);
4966 head.add_data(subsys->system_info->subobj_name);
4971 subsys = GET_NEXT(subsys);
4975 // if one of the subsystem strength operators, append the Hull string
4976 if(special_subsys == OPS_STRENGTH){
4977 head.add_data(SEXP_HULL_STRING);
4983 sexp_list_item *sexp_tree::get_listing_opf_point()
4985 char buf[NAME_LENGTH+8];
4987 sexp_list_item head;
4989 for (i=0; i<Num_waypoint_lists; i++)
4990 for (j=0; j<Waypoint_lists[i].count; j++) {
4991 sprintf(buf, "%s:%d", Waypoint_lists[i].name, j + 1);
4992 head.add_data_dup(buf);
4998 sexp_list_item *sexp_tree::get_listing_opf_iff()
5001 sexp_list_item head;
5003 for (i=0; i<Num_team_names; i++)
5004 head.add_data(Team_names[i]);
5009 sexp_list_item *sexp_tree::get_listing_opf_ai_goal(int parent_node)
5011 int i, n, w, z, child;
5012 sexp_list_item head;
5014 SDL_assert(parent_node >= 0);
5015 child = nodes[parent_node].child;
5016 SDL_assert(child >= 0);
5017 n = ship_name_lookup(nodes[child].text, 1);
5019 // add operators if it's an ai-goal and ai-goal is allowed for that ship
5020 for (i=0; i<Num_operators; i++) {
5021 if ( (query_operator_return_type(i) == OPR_AI_GOAL) && query_sexp_ai_goal_valid(Operators[i].value, n) )
5026 z = wing_name_lookup(nodes[child].text);
5028 for (w=0; w<Wings[z].wave_count; w++) {
5029 n = Wings[z].ship_index[w];
5030 // add operators if it's an ai-goal and ai-goal is allowed for that ship
5031 for (i=0; i<Num_operators; i++) {
5032 if ( (query_operator_return_type(i) == OPR_AI_GOAL) && query_sexp_ai_goal_valid(Operators[i].value, n) )
5038 return NULL; // no valid ship or wing to check against, make nothing available
5044 sexp_list_item *sexp_tree::get_listing_opf_docker_point(int parent_node)
5047 sexp_list_item head;
5049 SDL_assert(parent_node >= 0);
5050 SDL_assert(!stricmp(nodes[parent_node].text, "ai-dock"));
5052 z = nodes[parent_node].parent;
5054 SDL_assert(!stricmp(nodes[z].text, "add-ship-goal") || !stricmp(nodes[z].text, "add-wing-goal") || !stricmp(nodes[z].text, "add-goal"));
5059 sh = ship_name_lookup(nodes[z].text, 1);
5061 z = get_docking_list(Ships[sh].modelnum);
5063 head.add_data(Docking_bay_list[i]);
5069 sexp_list_item *sexp_tree::get_listing_opf_dockee_point(int parent_node)
5072 sexp_list_item head;
5074 SDL_assert(parent_node >= 0);
5075 SDL_assert(!stricmp(nodes[parent_node].text, "ai-dock"));
5077 z = nodes[parent_node].child;
5080 sh = ship_name_lookup(nodes[z].text, 1);
5082 z = get_docking_list(Ships[sh].modelnum);
5084 head.add_data(Docking_bay_list[i]);
5090 sexp_list_item *sexp_tree::get_listing_opf_message()
5094 sexp_list_item head;
5096 if (m_mode == MODE_EVENTS) {
5097 SDL_assert(Event_editor_dlg);
5098 // this for looks a litle strange, but had to do it get rid of a warning. Conditional
5099 //uses last statement is sequence, i.e. same as for (i=0, str, i++)
5100 for (i=0; str = Event_editor_dlg->current_message_name(i), str; i++)
5104 for (i=Num_builtin_messages; i<Num_messages; i++)
5105 head.add_data(Messages[i].name);
5111 sexp_list_item *sexp_tree::get_listing_opf_who_from()
5114 sexp_list_item head;
5116 //head.add_data("<any allied>");
5117 head.add_data("#Command");
5118 head.add_data("<any wingman>");
5120 ptr = GET_FIRST(&obj_used_list);
5121 while (ptr != END_OF_LIST(&obj_used_list)) {
5122 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START))
5123 if (!(Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].flags & SIF_NOT_FLYABLE))
5124 head.add_data(Ships[ptr->instance].ship_name);
5126 ptr = GET_NEXT(ptr);
5132 sexp_list_item *sexp_tree::get_listing_opf_priority()
5134 sexp_list_item head;
5136 head.add_data("High");
5137 head.add_data("Normal");
5138 head.add_data("Low");
5142 sexp_list_item *sexp_tree::get_listing_opf_waypoint_path()
5145 sexp_list_item head;
5147 for (i=0; i<Num_waypoint_lists; i++)
5148 head.add_data(Waypoint_lists[i].name);
5153 sexp_list_item *sexp_tree::get_listing_opf_ship_point()
5155 sexp_list_item head;
5157 head.add_list(get_listing_opf_ship());
5158 head.add_list(get_listing_opf_point());
5162 sexp_list_item *sexp_tree::get_listing_opf_ship_wing_point()
5164 sexp_list_item head;
5166 head.add_data("<any friendly>");
5167 head.add_data("<any hostile>");
5168 head.add_data("<any neutral>");
5169 head.add_data("<any unknown>");
5170 head.add_list(get_listing_opf_ship());
5171 head.add_list(get_listing_opf_wing());
5172 head.add_list(get_listing_opf_point());
5176 sexp_list_item *sexp_tree::get_listing_opf_mission_name()
5179 sexp_list_item head;
5181 if ((m_mode == MODE_CAMPAIGN) && (Cur_campaign_mission >= 0)) {
5182 for (i=0; i<Campaign.num_missions; i++)
5183 if ( (i == Cur_campaign_mission) || (Campaign.missions[i].level < Campaign.missions[Cur_campaign_mission].level) )
5184 head.add_data(Campaign.missions[i].name);
5187 head.add_data(Mission_filename);
5192 sexp_list_item *sexp_tree::get_listing_opf_goal_name(int parent_node)
5195 sexp_list_item head;
5197 if (m_mode == MODE_CAMPAIGN) {
5200 SDL_assert(parent_node >= 0);
5201 child = nodes[parent_node].child;
5202 SDL_assert(child >= 0);
5204 for (m=0; m<Campaign.num_missions; m++)
5205 if (!stricmp(Campaign.missions[m].name, nodes[child].text))
5208 if (m < Campaign.num_missions) {
5209 if (Campaign.missions[m].num_goals < 0) // haven't loaded goal names yet.
5210 read_mission_goal_list(m);
5212 for (i=0; i<Campaign.missions[m].num_goals; i++)
5213 head.add_data(Campaign.missions[m].goals[i].name);
5217 for (i=0; i<Num_goals; i++)
5218 head.add_data(Mission_goals[i].name);
5224 sexp_list_item *sexp_tree::get_listing_opf_ship_wing()
5226 sexp_list_item head;
5228 head.add_list(get_listing_opf_ship());
5229 head.add_list(get_listing_opf_wing());
5233 sexp_list_item *sexp_tree::get_listing_opf_ship_type()
5236 sexp_list_item head;
5238 for (i=0; i<MAX_SHIP_TYPE_COUNTS; i++){
5239 head.add_data(Ship_type_names[i]);
5245 sexp_list_item *sexp_tree::get_listing_opf_keypress()
5248 sexp_list_item head;
5250 for (i=0; i<CCFG_MAX; i++) {
5251 if (Control_config[i].key_default > 0) {
5252 head.add_data_dup(textify_scancode(Control_config[i].key_default));
5259 sexp_list_item *sexp_tree::get_listing_opf_event_name(int parent_node)
5262 sexp_list_item head;
5265 if (m_mode == MODE_CAMPAIGN) {
5268 SDL_assert(parent_node >= 0);
5269 child = nodes[parent_node].child;
5270 SDL_assert(child >= 0);
5272 for (m=0; m<Campaign.num_missions; m++)
5273 if (!stricmp(Campaign.missions[m].name, nodes[child].text))
5276 if (m < Campaign.num_missions) {
5277 if (Campaign.missions[m].num_events < 0) // haven't loaded goal names yet.
5278 read_mission_goal_list(m);
5280 for (i=0; i<Campaign.missions[m].num_events; i++)
5281 head.add_data(Campaign.missions[m].events[i].name);
5285 for (i=0; i<Num_mission_events; i++)
5286 head.add_data(Mission_events[i].name);
5292 sexp_list_item *sexp_tree::get_listing_opf_ai_order()
5295 sexp_list_item head;
5297 for (i=0; i<Fred_comm_orders_max; i++)
5298 head.add_data(Fred_comm_orders[i].menu_text);
5303 sexp_list_item *sexp_tree::get_listing_opf_skill_level()
5306 sexp_list_item head;
5308 for (i=0; i<NUM_SKILL_LEVELS; i++)
5309 head.add_data(Skill_level_names(i, 0));
5314 sexp_list_item *sexp_tree::get_listing_opf_medal_name()
5317 sexp_list_item head;
5319 for (i=0; i<MAX_ASSIGNABLE_MEDALS; i++)
5320 head.add_data(Medals[i].name);
5322 // also add SOC crest (index 17) and Wings (index 13)
5323 head.add_data(Medals[13].name);
5324 head.add_data(Medals[17].name);
5329 sexp_list_item *sexp_tree::get_listing_opf_weapon_name()
5332 sexp_list_item head;
5334 for (i=0; i<Num_weapon_types; i++)
5335 head.add_data(Weapon_info[i].name);
5340 sexp_list_item *sexp_tree::get_listing_opf_ship_class_name()
5343 sexp_list_item head;
5345 for (i=0; i<Num_ship_types; i++)
5346 head.add_data(Ship_info[i].name);
5351 sexp_list_item *sexp_tree::get_listing_opf_hud_gauge_name()
5354 sexp_list_item head;
5356 for (i=0; i<NUM_HUD_GAUGES; i++)
5357 head.add_data(HUD_gauge_text[i]);
5362 sexp_list_item *sexp_tree::get_listing_opf_huge_weapon()
5365 sexp_list_item head;
5367 for (i=0; i<Num_weapon_types; i++) {
5368 if (Weapon_info[i].wi_flags & WIF_HUGE)
5369 head.add_data(Weapon_info[i].name);
5375 sexp_list_item *sexp_tree::get_listing_opf_ship_not_player()
5378 sexp_list_item head;
5380 ptr = GET_FIRST(&obj_used_list);
5381 while (ptr != END_OF_LIST(&obj_used_list)) {
5382 if (ptr->type == OBJ_SHIP)
5383 head.add_data(Ships[ptr->instance].ship_name);
5385 ptr = GET_NEXT(ptr);
5391 sexp_list_item *sexp_tree::get_listing_opf_jump_nodes()
5394 sexp_list_item head;
5396 for (i = 0; i < Num_jump_nodes; i++ )
5397 head.add_data( Jump_nodes[i].name );
5402 // creates list of Sexp_variables
5403 sexp_list_item *sexp_tree::get_listing_opf_variable_names()
5406 sexp_list_item head;
5408 for (i=0; i<MAX_SEXP_VARIABLES; i++) {
5409 if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
5410 head.add_data( Sexp_variables[i].variable_name );
5418 // Deletes sexp_variable from sexp_tree.
5419 // resets tree to not include given variable, and resets text and type
5420 void sexp_tree::delete_sexp_tree_variable(const char *var_name)
5422 char search_str[64];
5423 char replace_text[TOKEN_LENGTH];
5425 sprintf(search_str, "%s(", var_name);
5427 // store old item index
5428 int old_item_index = item_index;
5430 for (int idx=0; idx<MAX_SEXP_TREE_SIZE; idx++) {
5431 if (nodes[idx].type & SEXPT_VARIABLE) {
5432 if ( strstr(nodes[idx].text, search_str) != NULL ) {
5434 // check type is number or string
5435 SDL_assert( (nodes[idx].type & SEXPT_NUMBER) || (nodes[idx].type & SEXPT_STRING) );
5437 // reset type as not variable
5438 int type = nodes[idx].type &= ~SEXPT_VARIABLE;
5441 if (nodes[idx].type & SEXPT_NUMBER) {
5442 strcpy(replace_text, "number");
5444 strcpy(replace_text, "string");
5447 // set item_index and replace data
5449 replace_data(replace_text, type);
5454 // restore item_index
5455 item_index = old_item_index;
5459 // Modify sexp_tree for a change in sexp_variable (name, type, or default value)
5460 void sexp_tree::modify_sexp_tree_variable(const char *old_name, int sexp_var_index)
5462 char search_str[64];
5465 SDL_assert(Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_SET);
5466 SDL_assert( (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) || (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) );
5468 // Get type for sexp_tree node
5469 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
5470 type = (SEXPT_NUMBER | SEXPT_VALID);
5472 type = (SEXPT_STRING | SEXPT_VALID);
5475 // store item index;
5476 int old_item_index = item_index;
5478 // Search string in sexp_tree nodes
5479 sprintf(search_str, "%s(", old_name);
5481 for (int idx=0; idx<MAX_SEXP_TREE_SIZE; idx++) {
5482 if (nodes[idx].type & SEXPT_VARIABLE) {
5483 if ( strstr(nodes[idx].text, search_str) != NULL ) {
5484 // temp set item_index
5487 // replace variable data
5488 replace_variable_data(sexp_var_index, (type | SEXPT_VARIABLE));
5493 // restore item_index
5494 item_index = old_item_index;
5498 // convert from item_index to sexp_variable index, -1 if not
5499 int sexp_tree::get_item_index_to_var_index()
5501 // check valid item index and node is a variable
5502 if ( (item_index > 0) && (nodes[item_index].type & SEXPT_VARIABLE) ) {
5504 return get_tree_name_to_sexp_variable_index(nodes[item_index].text);
5510 int sexp_tree::get_tree_name_to_sexp_variable_index(const char *tree_name)
5512 char var_name[TOKEN_LENGTH];
5514 int chars_to_copy = strcspn(tree_name, "(");
5515 SDL_assert(chars_to_copy < TOKEN_LENGTH - 1);
5517 // Copy up to '(' and add null termination
5518 strncpy(var_name, tree_name, chars_to_copy);
5519 var_name[chars_to_copy] = '\0';
5522 return get_index_sexp_variable_name(var_name);
5525 int sexp_tree::get_variable_count(const char *var_name)
5529 char compare_name[64];
5531 // get name to compare
5532 strcpy(compare_name, var_name);
5533 strcat(compare_name, "(");
5535 // look for compare name
5536 for (idx=0; idx<MAX_SEXP_TREE_SIZE; idx++) {
5537 if (nodes[idx].type & SEXPT_VARIABLE) {
5538 if ( strstr(nodes[idx].text, compare_name) ) {