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/parse/SEXP.CPP $
15 * main sexpression generator
18 * Revision 1.5 2003/05/25 02:30:43 taylor
21 * Revision 1.4 2002/06/17 06:15:25 relnev
22 * ryan's struct patch (and cr removal)
24 * Revision 1.3 2002/06/09 04:41:25 relnev
25 * added copyright header
27 * Revision 1.2 2002/05/07 03:16:48 theoddone33
28 * The Great Newline Fix
30 * Revision 1.1.1.1 2002/05/03 03:28:10 root
34 * 66 9/13/99 11:38a Andsager
35 * Stupid switch for sexp_team_score. Don't forget *break*
37 * 65 9/09/99 11:40p Dave
38 * Handle an SDL_assert() in beam code. Added supernova sounds. Play the right
39 * 2 end movies properly, based upon what the player did in the mission.
41 * 64 9/07/99 1:05a Andsager
42 * Added team-score sexp for multi team vs team missions
44 * 63 9/06/99 9:46p Jefff
45 * skip mission support
47 * 62 8/29/99 2:33p Andsager
48 * Fix bug in sexp_is_ship_visible()
50 * 61 8/27/99 9:07p Dave
51 * LOD explosions. Improved beam weapon accuracy.
53 * 60 8/27/99 4:07p Andsager
54 * Add is-ship-visible sexp. Make ship-vanish sexp SINGLE player only
56 * 59 8/26/99 4:21p Mikek
57 * Add new special-check for JimB, zeroing front shield quadrant.
59 * 58 8/24/99 4:25p Andsager
60 * Add ship-vanish sexp
62 * 57 8/23/99 6:05p Johnson
63 * Oops. Self destruct log should also go into the is-destroyed
66 * 56 8/23/99 3:23p Johnson
67 * Fix bug in sexp_facing2 when Player_obj does not yet existl
69 * 55 8/19/99 3:30p Andsager
70 * Make sexp_special_warp_dist check if ship if facing knossos device
72 * 54 8/19/99 9:20a Andsager
73 * Enable flashing for all guages
75 * 53 8/19/99 12:31a Andsager
76 * Add support guage as one that can flash
78 * 52 8/18/99 12:09p Andsager
79 * Add debug if message has no anim for message. Make messages come from
82 * 51 8/16/99 10:04p Andsager
83 * Add special-warp-dist and special-warpout-name sexp for Knossos device
86 * 50 8/09/99 3:32p Andsager
87 * Make has_time_elapsed work with arithmatic args.
89 * 49 8/09/99 2:00p Dave
92 * 48 8/02/99 4:26p Dave
93 * Added 2 new sexpressions.
95 * 47 7/31/99 2:30p Dave
96 * Added nifty mission message debug viewing keys.
98 * 46 7/28/99 1:36p Andsager
99 * Modify cargo1 to include flag CARGO_NO_DEPLETE. Add sexp
100 * cargo-no-deplete (only for BIG / HUGE). Modify ship struct to pack
103 * 45 7/24/99 4:56p Dave
104 * Added 3 new sexpressions.
106 * 44 7/21/99 8:10p Dave
107 * First run of supernova effect.
109 * 43 7/20/99 9:19p Andsager
110 * Added facing waypoint sexp
112 * 42 7/20/99 9:54a Andsager
113 * Add subsys-set-random sexp
115 * 41 7/19/99 12:02p Andsager
116 * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
117 * only blow up subsystem if its strength is > 0
119 * 40 7/15/99 9:22p Andsager
120 * modify secondaries_depleted sexp to report true only when all
123 * 39 7/13/99 3:37p Andsager
124 * Add secondaries-depleted sexp
126 * 38 7/08/99 12:06p Andsager
127 * Add turret-tagged-only and turret-tagged-clear sexp.
129 * 37 6/28/99 4:51p Andsager
130 * Add ship-guardian sexp (does not allow ship to be killed)
132 * 36 6/25/99 2:51p Andsager
133 * Make event_delay, goal_delay, and subsys strength work with sexp_rand
135 * 35 6/23/99 5:51p Andsager
136 * Add waypoint-cap-speed. Checkin stealth ai - inactive.
138 * 34 6/16/99 10:21a Dave
139 * Added send-message-list sexpression.
141 * 33 6/15/99 2:53p Andsager
142 * Fixed subsystem checking for AWACS type subsystems.
144 * 32 6/01/99 8:35p Dave
145 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
146 * awacs-set-radius sexpression.
148 * 31 5/27/99 12:14p Andsager
149 * Some fixes for live debris when more than one subsys on ship with live
150 * debris. Set subsys strength (when 0) blows off subsystem.
151 * sexp_hits_left_subsystem works for SUBSYSTEM_UNKNOWN.
153 * 30 5/24/99 11:28a Dave
154 * Sexpression for adding/removing ships from the hud escort list.
156 * 29 4/28/99 9:33a Andsager
157 * Add turret-free and turret-lock (and -all) sexp. Stargger start time
158 * of beam weapons beam-free and beam-free-all.
160 * 28 4/26/99 2:14p Andsager
161 * Add beam-protect-ship and beam-unprotect-ship sexp.
163 * 27 4/23/99 12:01p Johnson
164 * Added SIF_HUGE_SHIP
166 * 26 4/23/99 9:45a Andsager
167 * Modify rand_sexp to return the same value each time it is called on a
168 * particular node. This prevents random time delay defaulting to min.
170 * 25 4/21/99 6:15p Dave
171 * Did some serious housecleaning in the beam code. Made it ready to go
172 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
173 * a handy macro for recalculating collision pairs for a given object.
175 * 24 4/19/99 2:28p Johnson
176 * DA: Use ship_info flags, not ship_flags for ship type. DOH!!
178 * 23 4/19/99 11:46a Johnson
181 * 22 4/19/99 11:36a Johnson
182 * Don't display multiple warning messges when large ship gets more than 1
185 * 21 4/02/99 9:55a Dave
186 * Added a few more options in the weapons.tbl for beam weapons. Attempt
187 * at putting "pain" packets into multiplayer.
189 * 20 3/31/99 2:22p Johnson
190 * Fix sexp_repair_subsystem() to accept sexp increments
192 * 19 3/28/99 5:58p Dave
193 * Added early demo code. Make objects move. Nice and framerate
194 * independant, but not much else. Don't use yet unless you're me :)
196 * 18 3/20/99 3:46p Dave
197 * Added support for model-based background nebulae. Added 3 new
200 * 17 3/08/99 7:03p Dave
201 * First run of new object update system. Looks very promising.
203 * 16 3/04/99 6:09p Dave
204 * Added in sexpressions for firing beams and checking for if a ship is
207 * 15 3/04/99 9:22a Andsager
208 * Make escort list work with ship-is-visible. When not visible, dump,
209 * when becoming visible, maybe add.
211 * 14 2/26/99 6:01p Andsager
212 * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
214 * 13 2/26/99 4:14p Dave
215 * Put in the ability to have multiple shockwaves for ships.
217 * 12 2/11/99 5:22p Andsager
218 * Fixed bugs, generalized block Sexp_variables
220 * 11 2/11/99 2:15p Andsager
221 * Add ship explosion modification to FRED
223 * 10 1/25/99 4:29p Andsager
224 * Modify sexp_modify_variable() to handle type string. added quick out
225 * for multiplayer_client
227 * 9 1/25/99 8:11a Andsager
228 * Add sexp_modify_variable(). Changed syntax checking to allow, adding
229 * operator return type ambiguous
231 * 8 1/24/99 11:37p Dave
232 * First full rev of beam weapons. Very customizable. Removed some bogus
233 * Int3()'s in low level net code.
235 * 7 1/20/99 9:37a Andsager
237 * 6 1/19/99 3:57p Andsager
238 * Round 2 of variables
240 * 5 1/07/99 1:52p Andsager
241 * Initial check in of Sexp_variables
243 * 4 11/05/98 5:55p Dave
244 * Big pass at reducing #includes
246 * 3 10/13/98 9:29a Dave
247 * Started neatening up freespace.h. Many variables renamed and
248 * reorganized. Added AlphaColors.[h,cpp]
250 * 2 10/07/98 10:53a Dave
253 * 1 10/07/98 10:50a Dave
255 * 321 9/20/98 7:20p Dave
256 * Added CHANGE_IFF packet.
258 * 320 9/16/98 10:42a Hoffoss
259 * Added sexp node counting to fsm files for end user designers.
261 * 319 7/06/98 4:31p Hoffoss
262 * Fixed bug in sexp parsing when formula is ( ).
264 * 318 6/09/98 5:15p Lawrance
265 * French/German localization
267 * 317 5/26/98 11:54a Allender
268 * fix multiplayer problems and sexpression crash
270 * 316 5/23/98 3:16p Allender
271 * work on object update packet optimizations (a new updating system).
272 * Don't allow medals/promotions/badges when playing singple player
273 * missions through the simulator
275 * 315 5/20/98 1:34a Allender
276 * fixed observer problems. Removed some code offending BoundsCheccker.
277 * Fixed medals for multiplayer
279 * 314 5/17/98 12:46p Mike
280 * Change special-check functions for cueing rearm suggestion.
282 * 313 5/14/98 6:29p Hoffoss
283 * Fixed some warnings a release rebuild all turned up.
285 * 312 5/14/98 10:15a Allender
286 * add optional argument to prevous-goal/event operators to specify what
287 * sexpression should return when being played as a single mission
289 * 311 5/08/98 10:16a Lawrance
290 * Add new "ship attacking count" gauge
292 * 310 4/29/98 11:39p Allender
293 * fixed nasty problem with is-event-false always returning true right
299 // Parse a symbolic expression.
300 // These are identical to Lisp functions.
301 // It uses a very baggy format, allocating 16 characters per token, regardless
302 // of how many are used.
314 #include "freespace.h"
317 #include "missionlog.h"
318 #include "missionparse.h" // for p_object definition
319 #include "missionmessage.h"
320 #include "missiontraining.h"
322 #include "linklist.h"
323 #include "model.h" // for subsystem types
325 #include "missioncampaign.h"
326 #include "missiongoals.h"
327 #include "controlsconfig.h"
330 #include "gamesequence.h"
334 #include "hudmessage.h"
336 #include "redalert.h"
337 #include "jumpnode.h"
338 #include "hudshield.h"
340 #include "multimsgs.h"
341 #include "multiutil.h"
342 #include "hudescort.h"
344 #include "supernova.h"
348 #include "multi_team.h"
353 sexp_oper Operators[] = {
354 // Operator, Identity, Min / Max arguments
355 { "+", OP_PLUS, 2, INT_MAX },
356 { "-", OP_MINUS, 2, INT_MAX },
357 { "*", OP_MUL, 2, INT_MAX },
358 { "/", OP_DIV, 2, INT_MAX },
359 { "mod", OP_MOD, 2, INT_MAX },
360 { "rand", OP_RAND, 2, 2 },
362 { "true", OP_TRUE, 0, 0, },
363 { "false", OP_FALSE, 0, 0, },
364 { "and", OP_AND, 2, INT_MAX, },
365 { "and-in-sequence", OP_AND_IN_SEQUENCE, 2, INT_MAX, },
366 { "or", OP_OR, 2, INT_MAX, },
367 { "not", OP_NOT, 1, 1, },
368 { "=", OP_EQUALS, 2, INT_MAX, },
369 { ">", OP_GREATER_THAN, 2, 2, },
370 { "<", OP_LESS_THAN, 2, 2, },
371 { "is-iff", OP_IS_IFF, 2, INT_MAX, },
372 { "has-time-elapsed", OP_HAS_TIME_ELAPSED, 1, 1, },
373 { "modify-variable", OP_MODIFY_VARIABLE, 2, 2, },
375 { "is-goal-incomplete", OP_GOAL_INCOMPLETE, 1, 1, },
376 { "is-goal-true-delay", OP_GOAL_TRUE_DELAY, 2, 2, },
377 { "is-goal-false-delay", OP_GOAL_FALSE_DELAY, 2, 2, },
378 { "is-event-incomplete", OP_EVENT_INCOMPLETE, 1, 1, },
379 { "is-event-true-delay", OP_EVENT_TRUE_DELAY, 2, 2, },
380 { "is-event-false-delay", OP_EVENT_FALSE_DELAY, 2, 2, },
381 { "is-previous-goal-incomplete", OP_PREVIOUS_GOAL_INCOMPLETE, 2, 3, },
382 { "is-previous-goal-true", OP_PREVIOUS_GOAL_TRUE, 2, 3, },
383 { "is-previous-goal-false", OP_PREVIOUS_GOAL_FALSE, 2, 3, },
384 { "is-previous-event-incomplete", OP_PREVIOUS_EVENT_INCOMPLETE, 2, 3, },
385 { "is-previous-event-true", OP_PREVIOUS_EVENT_TRUE, 2, 3, },
386 { "is-previous-event-false", OP_PREVIOUS_EVENT_FALSE, 2, 3, },
388 { "is-destroyed", OP_IS_DESTROYED, 1, INT_MAX, },
389 { "is-subsystem-destroyed", OP_IS_SUBSYSTEM_DESTROYED, 2, 2, },
390 { "is-disabled", OP_IS_DISABLED, 1, INT_MAX, },
391 { "is-disarmed", OP_IS_DISARMED, 1, INT_MAX, },
392 { "has-docked", OP_HAS_DOCKED, 3, 3, },
393 { "has-undocked", OP_HAS_UNDOCKED, 3, 3, },
394 { "has-arrived", OP_HAS_ARRIVED, 1, INT_MAX, },
395 { "has-departed", OP_HAS_DEPARTED, 1, INT_MAX, },
396 { "is-destroyed-delay", OP_IS_DESTROYED_DELAY, 2, INT_MAX, },
397 { "is-subsystem-destroyed-delay", OP_IS_SUBSYSTEM_DESTROYED_DELAY, 3, 3, },
398 { "is-disabled-delay", OP_IS_DISABLED_DELAY, 2, INT_MAX, },
399 { "is-disarmed-delay", OP_IS_DISARMED_DELAY, 2, INT_MAX, },
400 { "has-docked-delay", OP_HAS_DOCKED_DELAY, 4, 4, },
401 { "has-undocked-delay", OP_HAS_UNDOCKED_DELAY, 4, 4, },
402 { "has-arrived-delay", OP_HAS_ARRIVED_DELAY, 2, INT_MAX, },
403 { "has-departed-delay", OP_HAS_DEPARTED_DELAY, 2, INT_MAX, },
404 { "are-waypoints-done", OP_WAYPOINTS_DONE, 2, 2, },
405 { "are-waypoints-done-delay", OP_WAYPOINTS_DONE_DELAY, 3, 3, },
406 { "ship-type-destroyed", OP_SHIP_TYPE_DESTROYED, 2, 2, },
407 { "is-event-true", OP_EVENT_TRUE, 1, 1, },
408 { "is-event-false", OP_EVENT_FALSE, 1, 1, },
409 { "is-cargo-known", OP_IS_CARGO_KNOWN, 1, INT_MAX, },
410 { "was-promotion-granted", OP_WAS_PROMOTION_GRANTED, 0, 1, },
411 { "was-medal-granted", OP_WAS_MEDAL_GRANTED, 0, 1, },
412 { "percent-ships-departed", OP_PERCENT_SHIPS_DEPARTED, 2, INT_MAX },
413 { "percent-ships-destroyed", OP_PERCENT_SHIPS_DESTROYED, 2, INT_MAX },
414 { "is-cargo-known-delay", OP_CARGO_KNOWN_DELAY, 2, INT_MAX, },
415 { "cap-subsys-cargo-known-delay", OP_CAP_SUBSYS_CARGO_KNOWN_DELAY, 3, INT_MAX, },
416 { "has-been-tagged-delay", OP_HAS_BEEN_TAGGED_DELAY, 2, INT_MAX, },
417 { "depart-node-delay", OP_DEPART_NODE_DELAY, 3, INT_MAX, },
418 { "destroyed-or-departed-delay", OP_DESTROYED_DEPARTED_DELAY, 2, INT_MAX, },
419 { "is_tagged", OP_IS_TAGGED, 1, 1 },
420 { "num_kills", OP_NUM_KILLS, 1, 1 },
421 { "num_type_kills", OP_NUM_TYPE_KILLS, 2, 2 },
422 { "num_class_kills", OP_NUM_CLASS_KILLS, 2, 2 },
423 { "shield-recharge-pct", OP_SHIELD_RECHARGE_PCT, 1, 1 },
424 { "engine-recharge-pct", OP_ENGINE_RECHARGE_PCT, 1, 1 },
425 { "weapon-recharge-pct", OP_WEAPON_RECHARGE_PCT, 1, 1 },
426 { "shield-quad-low", OP_SHIELD_QUAD_LOW, 2, 2 },
427 { "secondary-ammo-pct", OP_SECONDARY_AMMO_PCT, 2, 2 },
428 { "is-secondary-selected", OP_IS_SECONDARY_SELECTED, 2, 2 },
429 { "is-primary-selected", OP_IS_PRIMARY_SELECTED, 2, 2 },
431 { "time-ship-destroyed", OP_TIME_SHIP_DESTROYED, 1, 1, },
432 { "time-ship-arrived", OP_TIME_SHIP_ARRIVED, 1, 1, },
433 { "time-ship-departed", OP_TIME_SHIP_DEPARTED, 1, 1, },
434 { "time-wing-destroyed", OP_TIME_WING_DESTROYED, 1, 1, },
435 { "time-wing-arrived", OP_TIME_WING_ARRIVED, 1, 1, },
436 { "time-wing-departed", OP_TIME_WING_DEPARTED, 1, 1, },
437 { "mission-time", OP_MISSION_TIME, 0, 0, },
438 { "time-docked", OP_TIME_DOCKED, 3, 3, },
439 { "time-undocked", OP_TIME_UNDOCKED, 3, 3, },
441 { "shields-left", OP_SHIELDS_LEFT, 1, 1, },
442 { "hits-left", OP_HITS_LEFT, 1, 1, },
443 { "hits-left-subsystem", OP_HITS_LEFT_SUBSYSTEM, 2, 2, },
444 { "distance", OP_DISTANCE, 2, 2, },
445 { "is-ship-visible", OP_IS_SHIP_VISIBLE, 1, 1, },
446 { "team-score", OP_TEAM_SCORE, 1, 1, },
447 { "time-elapsed-last-order", OP_LAST_ORDER_TIME, 2, 2, /*INT_MAX*/ },
448 { "skill-level-at-least", OP_SKILL_LEVEL_AT_LEAST, 1, 1, },
449 { "num-players", OP_NUM_PLAYERS, 0, 0, },
450 { "special-warp-dist", OP_SPECIAL_WARP_DISTANCE, 1, 1, },
452 { "special-warpout-name", OP_SET_SPECIAL_WARPOUT_NAME, 2, 2 },
454 { "do-nothing", OP_NOP, 0, 0, },
455 { "when", OP_WHEN, 2, INT_MAX, },
456 { "cond", OP_COND, 1, INT_MAX, },
458 { "add-goal", OP_ADD_GOAL, 2, 2, },
459 { "add-ship-goal", OP_ADD_SHIP_GOAL, 2, 2, },
460 { "add-wing-goal", OP_ADD_WING_GOAL, 2, 2, },
461 { "clear-goals", OP_CLEAR_GOALS, 1, INT_MAX, },
462 { "clear-ship-goals", OP_CLEAR_SHIP_GOALS, 1, 1, },
463 { "clear-wing-goals", OP_CLEAR_WING_GOALS, 1, 1, },
464 { "change-iff", OP_CHANGE_IFF, 2, INT_MAX, },
465 { "protect-ship", OP_PROTECT_SHIP, 1, INT_MAX, },
466 { "unprotect-ship", OP_UNPROTECT_SHIP, 1, INT_MAX, },
467 { "beam-protect-ship", OP_BEAM_PROTECT_SHIP, 1, INT_MAX, },
468 { "beam-unprotect-ship", OP_BEAM_UNPROTECT_SHIP, 1, INT_MAX, },
469 { "send-message", OP_SEND_MESSAGE, 3, 3, },
470 { "self-destruct", OP_SELF_DESTRUCT, 1, INT_MAX, },
471 { "next-mission", OP_NEXT_MISSION, 1, 1, },
472 { "end-campaign", OP_END_CAMPAIGN, 0, 0, },
473 { "end-of-campaign", OP_END_OF_CAMPAIGN, 0, 0, },
474 { "sabotage-subsystem", OP_SABOTAGE_SUBSYSTEM, 3, 3, },
475 { "repair-subsystem", OP_REPAIR_SUBSYSTEM, 3, 3, },
476 { "set-subsystem-strength", OP_SET_SUBSYSTEM_STRNGTH, 3, 3, },
477 { "invalidate-goal", OP_INVALIDATE_GOAL, 1, INT_MAX, },
478 { "validate-goal", OP_VALIDATE_GOAL, 1, INT_MAX, },
479 { "send-random-message", OP_SEND_RANDOM_MESSAGE, 3, INT_MAX, },
480 { "transfer-cargo", OP_TRANSFER_CARGO, 2, 2, },
481 { "exchange-cargo", OP_EXCHANGE_CARGO, 2, 2, },
482 { "end-misison-delay", OP_END_MISSION_DELAY, 1, 1, },
483 { "good-rearm-time", OP_GOOD_REARM_TIME, 2, 2, },
484 { "grant-promotion", OP_GRANT_PROMOTION, 0, 0, },
485 { "grant-medal", OP_GRANT_MEDAL, 1, 1, },
486 { "allow-ship", OP_ALLOW_SHIP, 1, 1, },
487 { "allow-weapon", OP_ALLOW_WEAPON, 1, 1, },
488 { "break-warp", OP_WARP_BROKEN, 1, INT_MAX, },
489 { "fix-warp", OP_WARP_NOT_BROKEN, 1, INT_MAX, },
490 { "never-warp", OP_WARP_NEVER, 1, INT_MAX, },
491 { "allow-warp", OP_WARP_ALLOWED, 1, INT_MAX, },
492 { "good-secondary-time", OP_GOOD_SECONDARY_TIME, 4, 4, },
493 { "ship-invisible", OP_SHIP_INVISIBLE, 1, INT_MAX },
494 { "ship-visible", OP_SHIP_VISIBLE, 1, INT_MAX },
495 { "ship-invulnerable", OP_SHIP_INVULNERABLE, 1, INT_MAX },
496 { "ship-vulnerable", OP_SHIP_VULNERABLE, 1, INT_MAX },
497 { "red-alert", OP_RED_ALERT, 0, 0 },
498 { "tech-add-ships", OP_TECH_ADD_SHIP, 1, INT_MAX },
499 { "tech-add-weapons", OP_TECH_ADD_WEAPON, 1, INT_MAX },
500 { "jettison-cargo-delay", OP_JETTISON_CARGO, 2, 2 },
501 { "fire-beam", OP_BEAM_FIRE, 3, 4 },
502 { "beam-free", OP_BEAM_FREE, 2, INT_MAX },
503 { "beam-free-all", OP_BEAM_FREE_ALL, 1, 1 },
504 { "beam-lock", OP_BEAM_LOCK, 2, INT_MAX },
505 { "beam-lock-all", OP_BEAM_LOCK_ALL, 1, 1 },
506 { "turret-free", OP_TURRET_FREE, 2, INT_MAX },
507 { "turret-free-all", OP_TURRET_FREE_ALL, 1, 1 },
508 { "turret-lock", OP_TURRET_LOCK, 2, INT_MAX },
509 { "turret-lock-all", OP_TURRET_LOCK_ALL, 1, 1 },
510 { "add-remove-escort", OP_ADD_REMOVE_ESCORT, 2, 2 },
511 { "awacs-set-radius", OP_AWACS_SET_RADIUS, 3, 3 },
512 { "send-message-list", OP_SEND_MESSAGE_LIST, 4, INT_MAX },
513 { "cap-waypoint-speed", OP_CAP_WAYPOINT_SPEED, 2, 2 },
514 { "ship-guardian", OP_SHIP_GUARDIAN, 1, INT_MAX },
515 { "ship-no-guardian", OP_SHIP_NO_GUARDIAN, 1, INT_MAX },
516 { "ship-vanish", OP_SHIP_VANISH, 1, INT_MAX },
517 { "turret-tagged-only", OP_TURRET_TAGGED_ONLY_ALL, 1, 1 },
518 { "turret-tagged-clear", OP_TURRET_TAGGED_CLEAR_ALL, 1, 1 },
519 { "subsys-set-random", OP_SUBSYS_SET_RANDOM, 3, INT_MAX },
520 { "supernova-start", OP_SUPERNOVA_START, 1, 1 },
521 { "cargo-no-deplete", OP_CARGO_NO_DEPLETE, 1, 2 },
523 { "error", OP_INT3, 0, 0 },
525 { "ai-chase", OP_AI_CHASE, 2, 2, },
526 { "ai-chase-wing", OP_AI_CHASE_WING, 2, 2, },
527 { "ai-dock", OP_AI_DOCK, 4, 4, },
528 { "ai-undock", OP_AI_UNDOCK, 1, 1, },
529 { "ai-warp", OP_AI_WARP, 2, 2, },
530 { "ai-warp-out", OP_AI_WARP_OUT, 1, 1, },
531 { "ai-waypoints", OP_AI_WAYPOINTS, 2, 2, },
532 { "ai-waypoints-once", OP_AI_WAYPOINTS_ONCE, 2, 2, },
533 { "ai-destroy-subsystem", OP_AI_DESTROY_SUBSYS, 3, 3, },
534 { "ai-disable-ship", OP_AI_DISABLE_SHIP, 2, 2, },
535 { "ai-disarm-ship", OP_AI_DISARM_SHIP, 2, 2, },
536 { "ai-guard", OP_AI_GUARD, 2, 2, },
537 { "ai-chase-any", OP_AI_CHASE_ANY, 1, 1, },
538 { "ai-guard-wing", OP_AI_GUARD_WING, 2, 2, },
539 { "ai-evade-ship", OP_AI_EVADE_SHIP, 2, 2, },
540 { "ai-stay-near-ship", OP_AI_STAY_NEAR_SHIP, 2, 2, },
541 { "ai-keep-safe-distance", OP_AI_KEEP_SAFE_DISTANCE, 1, 1, },
542 { "ai-ignore", OP_AI_IGNORE, 2, 2, },
543 { "ai-stay-still", OP_AI_STAY_STILL, 2, 2, },
544 { "ai-play-dead", OP_AI_PLAY_DEAD, 1, 1, },
546 { "goals", OP_GOALS_ID, 1, INT_MAX, },
548 { "key-pressed", OP_KEY_PRESSED, 1, 2, },
549 { "key-reset", OP_KEY_RESET, 1, INT_MAX, },
550 { "targeted", OP_TARGETED, 1, 3, },
551 { "speed", OP_SPEED, 1, 1, },
552 { "facing", OP_FACING, 2, 2, },
553 { "facing-waypoint", OP_FACING2, 2, 2, },
554 { "order", OP_ORDER, 2, 2, },
555 { "waypoint-missed", OP_WAYPOINT_MISSED, 0, 0, },
556 { "waypoint-twice", OP_WAYPOINT_TWICE, 0, 0, },
557 { "path-flown", OP_PATH_FLOWN, 0, 0, },
558 { "training-msg", OP_TRAINING_MSG, 1, 4, },
559 { "flash-hud-gauge", OP_FLASH_HUD_GAUGE, 1, 1, },
560 { "special-check", OP_SPECIAL_CHECK, 1, 1, },
561 { "secondaries-depleted", OP_SECONDARIES_DEPLETED, 1, 1, },
563 { "set-training-context-fly-path", OP_SET_TRAINING_CONTEXT_FLY_PATH, 2, 2, },
564 { "set-training-context-speed", OP_SET_TRAINING_CONTEXT_SPEED, 2, 2, },
567 sexp_ai_goal_link Sexp_ai_goal_links[] = {
568 { AI_GOAL_CHASE, OP_AI_CHASE },
569 { AI_GOAL_CHASE_WING, OP_AI_CHASE_WING },
570 { AI_GOAL_DOCK, OP_AI_DOCK },
571 { AI_GOAL_UNDOCK, OP_AI_UNDOCK },
572 { AI_GOAL_WARP, OP_AI_WARP_OUT },
573 { AI_GOAL_WARP, OP_AI_WARP },
574 { AI_GOAL_WAYPOINTS, OP_AI_WAYPOINTS },
575 { AI_GOAL_WAYPOINTS_ONCE, OP_AI_WAYPOINTS_ONCE },
576 { AI_GOAL_DESTROY_SUBSYSTEM, OP_AI_DESTROY_SUBSYS },
577 { AI_GOAL_DISABLE_SHIP, OP_AI_DISABLE_SHIP },
578 { AI_GOAL_DISARM_SHIP, OP_AI_DISARM_SHIP },
579 { AI_GOAL_GUARD, OP_AI_GUARD },
580 { AI_GOAL_CHASE_ANY, OP_AI_CHASE_ANY },
581 { AI_GOAL_GUARD_WING, OP_AI_GUARD_WING },
582 { AI_GOAL_EVADE_SHIP, OP_AI_EVADE_SHIP },
583 { AI_GOAL_STAY_NEAR_SHIP, OP_AI_STAY_NEAR_SHIP },
584 { AI_GOAL_KEEP_SAFE_DISTANCE, OP_AI_KEEP_SAFE_DISTANCE },
585 { AI_GOAL_IGNORE, OP_AI_IGNORE },
586 { AI_GOAL_STAY_STILL, OP_AI_STAY_STILL },
587 { AI_GOAL_PLAY_DEAD, OP_AI_PLAY_DEAD },
590 const char *HUD_gauge_text[NUM_HUD_GAUGES] =
602 "TARGET_MONITOR_EXTRA_DATA",
603 "TARGET_SHIELD_ICON",
604 "PLAYER_SHIELD_ICON",
612 "AFTERBURNER_ENERGY",
614 "WEAPON_LINKING_GAUGE",
616 "OFFSCREEN_INDICATOR",
620 "MISSILE_WARNING_ARROW",
622 "OBJECTIVES_NOTIFY_GAUGE",
626 "ATTACKING TARGET COUNT",
635 int Sexp_useful_number; // a variable to pass useful info in from external modules
636 int Locked_sexp_true, Locked_sexp_false;
637 int Num_operators = sizeof(Operators) / sizeof(sexp_oper);
638 int Num_sexp_ai_goal_links = sizeof(Sexp_ai_goal_links) / sizeof(sexp_ai_goal_link);
640 int Sexp_clipboard = -1; // used by Fred
641 int Training_context = 0;
642 int Training_context_speed_set;
643 int Training_context_speed_min;
644 int Training_context_speed_max;
645 int Training_context_speed_timestamp;
646 int Training_context_path;
647 int Training_context_goal_waypoint;
648 int Training_context_at_waypoint;
649 float Training_context_distance;
650 char Sexp_error_text[MAX_SEXP_TEXT];
651 char *Sexp_string; //[1024] = {0};
653 sexp_node Sexp_nodes[MAX_SEXP_NODES];
654 sexp_variable Sexp_variables[MAX_SEXP_VARIABLES];
656 int Players_target = UNINITIALIZED;
657 ship_subsys *Players_targeted_subsys;
658 int Players_target_timestamp;
660 int get_sexp(char *token);
661 int eval_sexp(int cur_node);
662 void build_extended_sexp_string(int cur_node, int level, int mode);
663 void update_sexp_references(char *old_name, char *new_name, int format, int node);
664 int sexp_determine_team(char *subj);
665 int sexp_distance2(int obj1, char *subj);
666 int sexp_distance3(int obj1, int obj2);
667 int extract_sexp_variable_index(int node);
668 void init_sexp_vars();
669 int num_eval(int node);
675 for (i=0; i<MAX_SEXP_NODES; i++) {
676 if ( !(Sexp_nodes[i].type & SEXP_FLAG_PERSISTENT) ){
677 Sexp_nodes[i].type = SEXP_NOT_USED;
683 Locked_sexp_false = Locked_sexp_true = -1;
684 Locked_sexp_false = alloc_sexp("false", SEXP_LIST, SEXP_ATOM_OPERATOR, -1, -1);
685 SDL_assert(Locked_sexp_false != -1);
686 Sexp_nodes[Locked_sexp_false].type = SEXP_ATOM; // fix bypassing value
687 Locked_sexp_true = alloc_sexp("true", SEXP_LIST, SEXP_ATOM_OPERATOR, -1, -1);
688 SDL_assert(Locked_sexp_true != -1);
689 Sexp_nodes[Locked_sexp_true].type = SEXP_ATOM; // fix bypassing value
692 // allocates an sexp node.
693 int alloc_sexp(const char *text, int type, int subtype, int first, int rest)
697 i = find_operator(text);
698 if ((i == OP_TRUE) && (type == SEXP_ATOM) && (subtype == SEXP_ATOM_OPERATOR)) {
699 return Locked_sexp_true;
700 } else if ((i == OP_FALSE) && (type == SEXP_ATOM) && (subtype == SEXP_ATOM_OPERATOR)) {
701 return Locked_sexp_false;
704 i = find_free_sexp();
705 SDL_assert(i != Locked_sexp_true);
706 SDL_assert(i != Locked_sexp_false);
707 if (i == MAX_SEXP_NODES){
711 SDL_assert(strlen(text) < TOKEN_LENGTH);
712 SDL_strlcpy(Sexp_nodes[i].text, text, TOKEN_LENGTH);
713 SDL_assert(type >= 0);
714 Sexp_nodes[i].type = type;
715 Sexp_nodes[i].subtype = subtype;
716 Sexp_nodes[i].first = first;
717 Sexp_nodes[i].rest = rest;
718 Sexp_nodes[i].value = SEXP_UNKNOWN;
724 int count_free_sexp_nodes()
728 for (i=0; i<MAX_SEXP_NODES; i++) {
729 if (Sexp_nodes[i].type == SEXP_NOT_USED){
731 } else if (Sexp_nodes[i].type & SEXP_FLAG_PERSISTENT){
736 if (MAX_SEXP_NODES - f > Sexp_hwm) {
737 nprintf(("Sexp", "Sexp nodes: Free=%d, Used=%d, Persistent=%d\n", f, MAX_SEXP_NODES - f, p));
738 Sexp_hwm = MAX_SEXP_NODES - f;
744 // find the next free sexp and return it's index.
749 for (i=0; i<MAX_SEXP_NODES; i++){
750 if (Sexp_nodes[i].type == SEXP_NOT_USED){
756 //count_free_sexp_nodes();
759 SDL_assert(i != MAX_SEXP_NODES); // time to raise the limit..
760 if (i == MAX_SEXP_NODES){
767 // sexp_mark_persistent() marks a whole sexp tree with the persistent flag so that it won't
768 // get re-used between missions
769 void sexp_mark_persistent( int n )
775 // total hack because of the true/false locked sexps -- we should make those persistent as well
776 if ( (n == Locked_sexp_true) || (n == Locked_sexp_false) ){
780 SDL_assert( !(Sexp_nodes[n].type & SEXP_FLAG_PERSISTENT) );
781 Sexp_nodes[n].type |= SEXP_FLAG_PERSISTENT;
783 sexp_mark_persistent(Sexp_nodes[n].first);
784 sexp_mark_persistent(Sexp_nodes[n].rest);
788 // sexp_unmark_persistent() removes the persistent flag from all nodes in the tree
789 void sexp_unmark_persistent( int n )
795 if ( (n == Locked_sexp_true) || (n == Locked_sexp_false) ){
799 SDL_assert( Sexp_nodes[n].type & SEXP_FLAG_PERSISTENT );
800 Sexp_nodes[n].type &= ~SEXP_FLAG_PERSISTENT;
802 sexp_unmark_persistent(Sexp_nodes[n].first);
803 sexp_unmark_persistent(Sexp_nodes[n].rest);
806 // just frees up the specified sexp node, Leaves link chains untouched.
807 int free_one_sexp(int num)
809 SDL_assert(Sexp_nodes[num].type != SEXP_NOT_USED); // make sure it is actually used
810 SDL_assert( !(Sexp_nodes[num].type & SEXP_FLAG_PERSISTENT) );
812 if ((num == Locked_sexp_true) || (num == Locked_sexp_false)){
816 Sexp_nodes[num].type = SEXP_NOT_USED;
820 // frees a used sexp node, so it can be reused later. Should only be called on
821 // an atom or a list, and not an operator. If on a list, the list and everything
822 // in it will be freed (including the operator).
823 int free_sexp(int num)
825 int i, rest, count = 0;
827 SDL_assert(Sexp_nodes[num].type != SEXP_NOT_USED); // make sure it is actually used
828 SDL_assert( !(Sexp_nodes[num].type & SEXP_FLAG_PERSISTENT) );
830 if ((num == Locked_sexp_true) || (num == Locked_sexp_false) || (num == -1) ){
834 Sexp_nodes[num].type = SEXP_NOT_USED;
837 i = Sexp_nodes[num].first;
839 count += free_sexp(i);
840 i = Sexp_nodes[i].rest;
843 rest = Sexp_nodes[num].rest;
844 for (i=0; i<MAX_SEXP_NODES; i++) {
845 if (Sexp_nodes[i].first == num){
846 Sexp_nodes[i].first = rest;
849 if (Sexp_nodes[i].rest == num){
850 Sexp_nodes[i].rest = rest;
854 return count; // total elements freed up.
857 // used to free up an entire sexp tree. Because the root node is an operator, instead of
858 // a list, we can't simply call free_sexp(). This function should only be called on the
859 // root node of an sexp, otherwise the linking will get screwed up.
860 int free_sexp2(int num)
864 if ((num == -1) || (num == Locked_sexp_true) || (num == Locked_sexp_false)){
868 i = Sexp_nodes[num].rest;
870 count += free_sexp(i);
871 i = Sexp_nodes[i].rest;
874 count += free_sexp(num);
878 // This function resets the status of all the nodes in a tree, forcing them to all be
880 void flush_sexp_tree(int node)
886 Sexp_nodes[node].value = SEXP_UNKNOWN;
887 flush_sexp_tree(Sexp_nodes[node].first);
888 flush_sexp_tree(Sexp_nodes[node].rest);
891 int verify_sexp_tree(int node)
897 if ((Sexp_nodes[node].type == SEXP_NOT_USED) ||
898 (Sexp_nodes[node].first == node) ||
899 (Sexp_nodes[node].rest == node)) {
900 Error(LOCATION, "Sexp node is corrupt");
904 if (Sexp_nodes[node].first != -1){
905 verify_sexp_tree(Sexp_nodes[node].first);
907 if (Sexp_nodes[node].rest != -1){
908 verify_sexp_tree(Sexp_nodes[node].rest);
914 int dup_sexp_chain(int node)
916 int cur, first, rest;
922 // TODO - CASE OF SEXP VARIABLES - ONLY 1 COPY OF VARIABLE
923 first = dup_sexp_chain(Sexp_nodes[node].first);
924 rest = dup_sexp_chain(Sexp_nodes[node].rest);
925 cur = alloc_sexp(Sexp_nodes[node].text, Sexp_nodes[node].type, Sexp_nodes[node].subtype, first, rest);
939 // returns 1 if they are the same, 0 if different
940 int cmp_sexp_chains(int node1, int node2)
942 if ((node1 == -1) && (node2 == -1)){
946 if ((node1 == -1) || (node2 == -1)){
950 // DA: 1/7/99 Need to check the actual Sexp_node.text, not possible variable, which can be equal
951 if (SDL_strcasecmp(Sexp_nodes[node1].text, Sexp_nodes[node2].text)){
955 if (!cmp_sexp_chains(Sexp_nodes[node1].first, Sexp_nodes[node2].first)){
959 if (!cmp_sexp_chains(Sexp_nodes[node1].rest, Sexp_nodes[node2].rest)){
966 // determine if an sexp node is within the given sexp chain.
967 int query_node_in_sexp(int node, int sexp)
976 if (query_node_in_sexp(node, Sexp_nodes[sexp].first)){
979 if (query_node_in_sexp(node, Sexp_nodes[sexp].rest)){
986 // find the index of the list associated with an operator
987 int find_sexp_list(int num)
991 for (i=0; i<MAX_SEXP_NODES; i++){
992 if (Sexp_nodes[i].first == num){
1000 // find index of operator that item is an argument of.
1001 int find_parent_operator(int num)
1005 if (Sexp_nodes[num].subtype == SEXP_ATOM_OPERATOR){
1006 num = find_sexp_list(num);
1009 while (Sexp_nodes[num].subtype != SEXP_ATOM_OPERATOR) {
1010 for (i=0; i<MAX_SEXP_NODES; i++){
1011 if (Sexp_nodes[i].rest == num){
1016 if (i == MAX_SEXP_NODES){
1017 return -1; // not found, probably at top node already.
1026 // function to determine if an sexpression node is the top level node of an sexpression tree. Top
1027 // level nodes do not have their node id in anyone elses first or rest index
1028 int is_sexp_top_level( int node )
1032 if ( Sexp_nodes[node].type == SEXP_NOT_USED ){
1036 for (i = 0; i < MAX_SEXP_NODES; i++ ) {
1037 if ( (Sexp_nodes[i].type == SEXP_NOT_USED) || ( i == node ) ){ // don't check myself or unused nodes
1040 if ( (Sexp_nodes[i].first == node) || (Sexp_nodes[i].rest == node) ){
1048 int identify_operator(const char *token)
1052 for (i=0; i<Num_operators; i++){
1053 if (!SDL_strcasecmp(token, Operators[i].text)){
1061 int find_operator(const char *token)
1065 for (i=0; i<Num_operators; i++){
1066 if (!SDL_strcasecmp(token, Operators[i].text)){
1067 return Operators[i].value;
1074 int query_sexp_args_count(int index)
1078 while (Sexp_nodes[index].rest != -1){
1080 index = Sexp_nodes[index].rest;
1086 // returns 0 if ok, negative if there's an error in expression..
1087 // See the returns types in sexp.h
1089 int check_sexp_syntax(int index, int return_type, int recursive, int *bad_index, int mode)
1091 int i = 0, z, t, type, argnum = 0, count, op, type2 = 0, op2;
1094 SDL_assert(index >= 0 && index < MAX_SEXP_NODES);
1095 SDL_assert(Sexp_nodes[index].type != SEXP_NOT_USED);
1096 if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER && return_type == OPR_BOOL) {
1097 // special case Mark seems to want supported
1098 SDL_assert(Sexp_nodes[index].first == -1); // only lists should have a first pointer
1099 if (Sexp_nodes[index].rest != -1) // anything after the number?
1100 return SEXP_CHECK_NONOP_ARGS; // if so, it's a syntax error
1105 op_index = index; // save the index of the operator since we need to get to other args.
1107 *bad_index = op_index;
1109 if (Sexp_nodes[op_index].subtype != SEXP_ATOM_OPERATOR)
1110 return SEXP_CHECK_OP_EXPTECTED; // not an operator, which it should always be
1112 op = identify_operator(CTEXT(op_index));
1114 return SEXP_CHECK_UNKNOWN_OP; // unrecognized operator
1116 //special case - OPR_AMBIGUOUS matches all
1117 if (return_type != OPR_AMBIGUOUS) {
1118 if (query_operator_return_type(op) != return_type) {
1119 return SEXP_CHECK_TYPE_MISMATCH;
1123 count = query_sexp_args_count(op_index);
1124 if (count < Operators[op].min || count > Operators[op].max)
1125 return SEXP_CHECK_BAD_ARG_COUNT; // incorrect number of arguments
1127 index = Sexp_nodes[op_index].rest;
1128 while (index != -1) {
1129 type = query_operator_argument_type(op, argnum);
1130 SDL_assert(Sexp_nodes[index].type != SEXP_NOT_USED);
1134 if (Sexp_nodes[index].subtype == SEXP_ATOM_LIST) {
1135 i = Sexp_nodes[index].first;
1139 // be sure to check to see if this node is a list of stuff and not an actual operator type
1140 // thing. (i.e. in the case of a cond statement, the conditional will fall into this if
1141 // statement. MORE TO DO HERE!!!!
1142 if (Sexp_nodes[i].subtype == SEXP_ATOM_LIST)
1145 op2 = identify_operator(CTEXT(i));
1147 return SEXP_CHECK_UNKNOWN_OP;
1149 type2 = query_operator_return_type(op2);
1172 // special case for modify-variable"
1178 return SEXP_CHECK_UNKNOWN_TYPE; // no other return types available
1181 if ((z = check_sexp_syntax(i, t, recursive, bad_index)) != 0) {
1186 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) {
1189 type2 = OPR_POSITIVE;
1196 if (type == OPF_BOOL) // allow numbers to be used where boolean is required.
1201 return SEXP_CHECK_INVALID_NUM; // not a valid number
1206 i = atoi(CTEXT(index));
1207 z = find_operator(CTEXT(op_index));
1208 if ( (z == OP_HAS_DOCKED_DELAY) || (z == OP_HAS_UNDOCKED_DELAY) )
1209 if ( (argnum == 2) && (i < 1) )
1210 return SEXP_CHECK_NUM_RANGE_INVALID;
1212 z = identify_operator(CTEXT(op_index));
1213 if ( (query_operator_return_type(z) == OPR_AI_GOAL) && (argnum == Operators[op].min - 1) )
1214 if ( (i < 0) || (i > 89) )
1215 return SEXP_CHECK_NUM_RANGE_INVALID;
1217 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_STRING) {
1218 type2 = SEXP_ATOM_STRING;
1226 if ((type2 != OPR_NUMBER) && (type2 != OPR_POSITIVE)){
1227 return SEXP_CHECK_TYPE_MISMATCH;
1233 if (type2 == OPR_NUMBER){
1234 return SEXP_CHECK_NEGATIVE_NUM;
1237 if (type2 != OPR_POSITIVE){
1238 return SEXP_CHECK_TYPE_MISMATCH;
1243 case OPF_SHIP_NOT_PLAYER:
1244 if (type2 != SEXP_ATOM_STRING){
1245 return SEXP_CHECK_TYPE_MISMATCH;
1248 if (ship_name_lookup(CTEXT(index), 0) < 0) {
1249 if (Fred_running || mission_parse_ship_arrived(CTEXT(index))){ // == 0 when still on arrival list
1250 return SEXP_CHECK_INVALID_SHIP;
1257 case OPF_SHIP_POINT:
1258 if (type2 != SEXP_ATOM_STRING){
1259 return SEXP_CHECK_TYPE_MISMATCH;
1262 if (ship_name_lookup(CTEXT(index), 1) < 0) {
1263 if (Fred_running || mission_parse_ship_arrived(CTEXT(index))) { // == 0 when still on arrival list
1264 if (type == OPF_SHIP){ // return invalid ship if not also looking for point
1265 return SEXP_CHECK_INVALID_SHIP;
1268 if (waypoint_lookup(CTEXT(index)) < 0){
1269 if (verify_vector(CTEXT(index))){ // verify return non-zero on invalid point
1270 return SEXP_CHECK_INVALID_POINT;
1279 if (type2 != SEXP_ATOM_STRING){
1280 return SEXP_CHECK_TYPE_MISMATCH;
1283 if (wing_name_lookup(CTEXT(index), 1) < 0){
1284 return SEXP_CHECK_INVALID_WING;
1290 case OPF_SHIP_WING_POINT:
1291 if ( type2 != SEXP_ATOM_STRING ){
1292 return SEXP_CHECK_TYPE_MISMATCH;
1295 if ((ship_name_lookup(CTEXT(index), 1) < 0) && (wing_name_lookup(CTEXT(index), 1) < 0)) {
1296 if (Fred_running || mission_parse_ship_arrived(CTEXT(index))) { // == 0 when still on arrival list
1297 if (type != OPF_SHIP_WING_POINT){ // return invalid if not also looking for point
1298 return SEXP_CHECK_INVALID_SHIP_WING;
1301 if (waypoint_lookup(CTEXT(index)) < 0){
1302 if (verify_vector(CTEXT(index))){ // non-zero on verify vector mean invalid!
1303 if (!sexp_determine_team(CTEXT(index))){
1304 return SEXP_CHECK_INVALID_POINT;
1313 case OPF_AWACS_SUBSYSTEM:
1314 case OPF_SUBSYSTEM: {
1316 int shipnum,ship_class, i;
1319 if (type2 != SEXP_ATOM_STRING){
1320 return SEXP_CHECK_TYPE_MISMATCH;
1323 // we must get the model of the ship that is part of this sexpression and find a subsystem
1324 // with that name. This code assumes that the ship *always* preceeds the subsystem in an sexpression.
1325 // if this current fact is ever not the case, the next lines of code must be changes to get the correct
1327 switch(Operators[identify_operator(CTEXT(op_index))].value){
1328 case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
1329 ship_index = Sexp_nodes[Sexp_nodes[op_index].rest].rest;
1334 ship_index = Sexp_nodes[op_index].rest;
1336 ship_index = Sexp_nodes[Sexp_nodes[Sexp_nodes[op_index].rest].rest].rest;
1341 ship_index = Sexp_nodes[op_index].rest;
1345 shipname = CTEXT(ship_index);
1346 shipnum = ship_name_lookup(shipname);
1348 ship_class = Ships[shipnum].ship_info_index;
1350 // must try to find the ship in the ship_arrival_list
1351 p_object *parse_obj;
1353 parse_obj = mission_parse_get_arrival_ship( shipname );
1354 if ( parse_obj == NULL ){
1355 return SEXP_CHECK_INVALID_SHIP;
1358 ship_class = parse_obj->ship_class;
1361 // check for the special "hull" value
1362 if ( (Operators[op].value == OP_SABOTAGE_SUBSYSTEM) || (Operators[op].value == OP_REPAIR_SUBSYSTEM) || (Operators[op].value == OP_SET_SUBSYSTEM_STRNGTH) ) {
1363 if ( !SDL_strcasecmp( CTEXT(index), SEXP_HULL_STRING) ){
1368 for (i=0; i<Ship_info[ship_class].n_subsystems; i++){
1369 if (!SDL_strcasecmp(Ship_info[ship_class].subsystems[i].subobj_name, CTEXT(index))){
1374 if (i == Ship_info[ship_class].n_subsystems){
1375 return SEXP_CHECK_INVALID_SUBSYS;
1378 // if we're checking for an AWACS subsystem and this is not an awacs subsystem
1380 if((type == OPF_AWACS_SUBSYSTEM) && !(Ship_info[ship_class].subsystems[i].flags & MSS_FLAG_AWACS)){
1381 return SEXP_CHECK_INVALID_SUBSYS;
1389 if (waypoint_lookup(CTEXT(index)) < 0){
1390 if (verify_vector(CTEXT(index))){
1391 return SEXP_CHECK_INVALID_POINT;
1395 if (type2 != SEXP_ATOM_STRING){
1396 return SEXP_CHECK_TYPE_MISMATCH;
1402 if (type2 == SEXP_ATOM_STRING){
1403 for (i=0; i<Num_team_names; i++){
1404 if (!SDL_strcasecmp(Team_names[i], CTEXT(index))){
1410 if (i == Num_team_names){
1411 return SEXP_CHECK_INVALID_IFF;
1414 if (type2 != SEXP_ATOM_STRING){
1415 return SEXP_CHECK_TYPE_MISMATCH;
1421 if (type2 != OPR_BOOL){
1422 return SEXP_CHECK_TYPE_MISMATCH;
1428 if (type2 != OPR_NULL){
1429 return SEXP_CHECK_TYPE_MISMATCH;
1435 if ( type2 != SEXP_ATOM_STRING ){
1436 return SEXP_CHECK_TYPE_MISMATCH;
1442 if (type2 != OPR_AI_GOAL){
1443 return SEXP_CHECK_TYPE_MISMATCH;
1447 int ship_num, ship2, i, w = 0, z;
1449 ship_num = ship_name_lookup(CTEXT(Sexp_nodes[op_index].rest));
1451 w = wing_name_lookup(CTEXT(Sexp_nodes[op_index].rest));
1454 *bad_index = Sexp_nodes[op_index].rest;
1457 return SEXP_CHECK_INVALID_SHIP; // should have already been caught earlier, but just in case..
1461 SDL_assert(Sexp_nodes[index].subtype == SEXP_ATOM_LIST);
1462 z = Sexp_nodes[index].first;
1463 SDL_assert(Sexp_nodes[z].subtype != SEXP_ATOM_LIST);
1464 z = find_operator(CTEXT(z));
1465 if (ship_num >= 0) {
1466 if (!query_sexp_ai_goal_valid(z, ship_num)){
1467 return SEXP_CHECK_ORDER_NOT_ALLOWED;
1471 for (i=0; i<Wings[w].wave_count; i++){
1472 if (!query_sexp_ai_goal_valid(z, Wings[w].ship_index[i])){
1473 return SEXP_CHECK_ORDER_NOT_ALLOWED;
1478 if ((z == OP_AI_DOCK) && (Sexp_nodes[index].rest >= 0)) {
1479 ship2 = ship_name_lookup(CTEXT(Sexp_nodes[index].rest));
1480 if ((ship_num < 0) || !ship_docking_valid(ship_num, ship2)){
1481 return SEXP_CHECK_DOCKING_NOT_ALLOWED;
1486 // we should check the syntax of the actual goal!!!!
1487 z = Sexp_nodes[index].first;
1488 if ((z = check_sexp_syntax(z, OPR_AI_GOAL, recursive, bad_index)) != 0){
1495 if (type2 != SEXP_ATOM_STRING){
1496 return SEXP_CHECK_TYPE_MISMATCH;
1499 for (i=0; i<MAX_SHIP_TYPE_COUNTS; i++){
1500 if (!SDL_strcasecmp( Ship_type_names[i], CTEXT(index))){
1505 if (i == MAX_SHIP_TYPE_COUNTS){
1506 return SEXP_CHECK_INVALID_SHIP_TYPE;
1511 case OPF_WAYPOINT_PATH:
1512 for (i=0; i<Num_waypoint_lists; i++){
1513 if (!SDL_strcasecmp(Waypoint_lists[i].name, CTEXT(index))){
1518 if (i == Num_waypoint_lists){
1519 return SEXP_CHECK_TYPE_MISMATCH;
1524 // code commented out because of order reversing of arguments to messaging code. Maybe
1525 // I'll comment it back in someday when older missions get fixed.
1526 if (type2 != SEXP_ATOM_STRING)
1527 return SEXP_CHECK_TYPE_MISMATCH;
1530 for (i=0; i<Num_messages; i++)
1531 if (!SDL_strcasecmp(Messages[i].name, CTEXT(index)))
1534 if (i == Num_messages)
1535 return SEXP_CHECK_UNKNOWN_MESSAGE;
1540 case OPF_PRIORITY: {
1541 // following code must be removed since I changed the order of the operators in the send-message
1542 // function to take priority second. Maybe someday, I'll require that all designers fix the
1544 if (type2 != SEXP_ATOM_STRING)
1545 return SEXP_CHECK_TYPE_MISMATCH;
1547 if (Fred_running) { // should still check in Fred though..
1550 name = CTEXT(index);
1551 if (!SDL_strcasecmp(name, "low") || !SDL_strcasecmp(name, "normal") || !SDL_strcasecmp(name, "high"))
1554 return SEXP_CHECK_INVALID_PRIORITY;
1560 case OPF_MISSION_NAME:
1561 if (type2 != SEXP_ATOM_STRING)
1562 return SEXP_CHECK_TYPE_MISMATCH;
1565 if (mode == SEXP_MODE_CAMPAIGN) {
1566 for (i=0; i<Campaign.num_missions; i++)
1567 if (!SDL_strcasecmp(CTEXT(index), Campaign.missions[i].name)) {
1568 if ((i != Sexp_useful_number) && (Campaign.missions[i].level >= Campaign.missions[Sexp_useful_number].level))
1569 return SEXP_CHECK_INVALID_LEVEL;
1574 if (i == Campaign.num_missions)
1575 return SEXP_CHECK_INVALID_MISSION_NAME;
1578 // mwa -- put the following if statement to prevent Fred errors for possibly valid
1579 // conditions. We should do something else here!!!
1580 if ( (Operators[op].value == OP_PREVIOUS_EVENT_TRUE) || (Operators[op].value == OP_PREVIOUS_EVENT_FALSE) || (Operators[op].value == OP_PREVIOUS_EVENT_INCOMPLETE)
1581 || (Operators[op].value == OP_PREVIOUS_GOAL_TRUE) || (Operators[op].value == OP_PREVIOUS_GOAL_FALSE) || (Operators[op].value == OP_PREVIOUS_GOAL_INCOMPLETE) )
1584 if (!(*Mission_filename) || SDL_strcasecmp(Mission_filename, CTEXT(index)))
1585 return SEXP_CHECK_INVALID_MISSION_NAME;
1592 if (type2 != SEXP_ATOM_STRING)
1593 return SEXP_CHECK_TYPE_MISMATCH;
1595 // we only need to check the campaign list if running in Fred and are in campaign mode.
1596 // otherwise, check the set of current goals
1597 if ( Fred_running && (mode == SEXP_MODE_CAMPAIGN) ) {
1598 z = find_parent_operator(index);
1600 z = Sexp_nodes[z].rest; // first argument of operator should be mission name
1602 for (i=0; i<Campaign.num_missions; i++)
1603 if (!SDL_strcasecmp(CTEXT(z), Campaign.missions[i].name))
1606 // read the goal/event list from the mission file if both num_goals and num_events
1608 if ((Campaign.missions[i].num_goals <= 0) && (Campaign.missions[i].num_events <= 0) )
1609 read_mission_goal_list(i);
1611 if (i < Campaign.num_missions) {
1612 for (t=0; t<Campaign.missions[i].num_goals; t++)
1613 if (!SDL_strcasecmp(CTEXT(index), Campaign.missions[i].goals[t].name))
1616 if (t == Campaign.missions[i].num_goals)
1617 return SEXP_CHECK_INVALID_GOAL_NAME;
1620 // MWA -- short circuit evaluation of these things for now.
1621 if ( (Operators[op].value == OP_PREVIOUS_GOAL_TRUE) || (Operators[op].value == OP_PREVIOUS_GOAL_FALSE) || (Operators[op].value == OP_PREVIOUS_GOAL_INCOMPLETE) )
1624 for (i=0; i<Num_goals; i++)
1625 if (!SDL_strcasecmp(CTEXT(index), Mission_goals[i].name))
1629 return SEXP_CHECK_INVALID_GOAL_NAME;
1634 case OPF_EVENT_NAME:
1635 if ( type2 != SEXP_ATOM_STRING )
1636 return SEXP_CHECK_TYPE_MISMATCH;
1638 // like above checking for goals, check events in the campaign only if in Fred
1639 // and only if in campaign mode. Otherwise, check the current set of events
1640 if ( Fred_running && (mode == SEXP_MODE_CAMPAIGN) ) {
1641 z = find_parent_operator(index);
1643 z = Sexp_nodes[z].rest; // first argument of operator should be mission name
1645 for (i=0; i<Campaign.num_missions; i++)
1646 if (!SDL_strcasecmp(CTEXT(z), Campaign.missions[i].name))
1649 // read the goal/event list from the mission file if both num_goals and num_events
1651 if ((Campaign.missions[i].num_goals <= 0) && (Campaign.missions[i].num_events <= 0) )
1652 read_mission_goal_list(i);
1654 if (i < Campaign.num_missions) {
1655 for (t=0; t<Campaign.missions[i].num_events; t++)
1656 if (!SDL_strcasecmp(CTEXT(index), Campaign.missions[i].events[t].name))
1659 if (t == Campaign.missions[i].num_events)
1660 return SEXP_CHECK_INVALID_EVENT_NAME;
1663 // MWA -- short circuit evaluation of these things for now.
1664 if ( (Operators[op].value == OP_PREVIOUS_EVENT_TRUE) || (Operators[op].value == OP_PREVIOUS_EVENT_FALSE) || (Operators[op].value == OP_PREVIOUS_EVENT_INCOMPLETE) )
1667 for ( i = 0; i < Num_mission_events; i++ ) {
1668 if ( !SDL_strcasecmp(CTEXT(index), Mission_events[i].name) )
1671 if ( i == Num_mission_events )
1672 return SEXP_CHECK_INVALID_EVENT_NAME;
1676 case OPF_DOCKER_POINT:
1677 if (type2 != SEXP_ATOM_STRING)
1678 return SEXP_CHECK_TYPE_MISMATCH;
1681 int ship_num, model, i, z;
1683 z = find_parent_operator(op_index);
1684 ship_num = ship_name_lookup(CTEXT(Sexp_nodes[z].rest));
1687 *bad_index = Sexp_nodes[z].rest;
1689 return SEXP_CHECK_INVALID_SHIP; // should have already been caught earlier, but just in case..
1692 model = Ships[ship_num].modelnum;
1693 z = model_get_num_dock_points(model);
1695 if (!SDL_strcasecmp(CTEXT(index), model_get_dock_name(model, i)))
1699 return SEXP_CHECK_INVALID_DOCKER_POINT;
1704 case OPF_DOCKEE_POINT:
1705 if (type2 != SEXP_ATOM_STRING)
1706 return SEXP_CHECK_TYPE_MISMATCH;
1709 int ship_num, model, i, z;
1711 ship_num = ship_name_lookup(CTEXT(Sexp_nodes[op_index].rest));
1714 *bad_index = Sexp_nodes[op_index].rest;
1716 return SEXP_CHECK_INVALID_SHIP; // should have already been caught earlier, but just in case..
1719 model = Ships[ship_num].modelnum;
1720 z = model_get_num_dock_points(model);
1722 if (!SDL_strcasecmp(CTEXT(index), model_get_dock_name(model, i)))
1726 return SEXP_CHECK_INVALID_DOCKEE_POINT;
1732 if (type2 != SEXP_ATOM_STRING)
1733 return SEXP_CHECK_TYPE_MISMATCH;
1735 if (*CTEXT(index) != '#') { // not a manual source?
1736 //if ( !SDL_strcasecmp(CTEXT(index), "<any allied>") )
1737 // return SEXP_CHECK_INVALID_MSG_SOURCE;
1739 if ( SDL_strcasecmp(CTEXT(index), "<any wingman>")) // not a special token?
1740 if ((ship_name_lookup(CTEXT(index)) < 0) && (wing_name_lookup(CTEXT(index), 1) < 0)) // is it in the mission?
1741 if (Fred_running || mission_parse_ship_arrived(CTEXT(index))) // == 0 when still on arrival list
1742 return SEXP_CHECK_INVALID_MSG_SOURCE;
1748 if (type2 != SEXP_ATOM_STRING)
1749 return SEXP_CHECK_TYPE_MISMATCH;
1753 case OPF_SKILL_LEVEL:
1754 if ( type2 != SEXP_ATOM_STRING )
1755 return SEXP_CHECK_TYPE_MISMATCH;
1757 for (i = 0; i < NUM_SKILL_LEVELS; i++) {
1758 if ( !SDL_strcasecmp(CTEXT(index), Skill_level_names(i, 0)) )
1761 if ( i == NUM_SKILL_LEVELS )
1762 return SEXP_CHECK_INVALID_SKILL_LEVEL;
1765 case OPF_MEDAL_NAME:
1766 if ( type2 != SEXP_ATOM_STRING)
1767 return SEXP_CHECK_TYPE_MISMATCH;
1769 for (i = 0; i < NUM_MEDALS; i++) {
1770 if ( !SDL_strcasecmp(CTEXT(index), Medals[i].name) )
1774 if ( i == NUM_MEDALS )
1775 return SEXP_CHECK_INVALID_MEDAL_NAME;
1778 case OPF_HUGE_WEAPON:
1779 case OPF_WEAPON_NAME:
1780 if ( type2 != SEXP_ATOM_STRING )
1781 return SEXP_CHECK_TYPE_MISMATCH;
1783 for (i = 0; i < Num_weapon_types; i++ ) {
1784 if ( !SDL_strcasecmp(CTEXT(index), Weapon_info[i].name) )
1788 if ( i == Num_weapon_types )
1789 return SEXP_CHECK_INVALID_WEAPON_NAME;
1791 // we need to be sure that for huge weapons, the WIF_HUGE flag is set
1792 if ( type == OPF_HUGE_WEAPON ) {
1793 if ( !(Weapon_info[i].wi_flags & WIF_HUGE) )
1794 return SEXP_CHECK_INVALID_WEAPON_NAME;
1799 case OPF_SHIP_CLASS_NAME:
1800 if ( type2 != SEXP_ATOM_STRING )
1801 return SEXP_CHECK_TYPE_MISMATCH;
1803 for (i = 0; i < Num_ship_types; i++ ) {
1804 if ( !SDL_strcasecmp(CTEXT(index), Ship_info[i].name) )
1808 if ( i == Num_ship_types )
1809 return SEXP_CHECK_INVALID_SHIP_CLASS_NAME;
1812 case OPF_HUD_GAUGE_NAME:
1813 if ( type2 != SEXP_ATOM_STRING )
1814 return SEXP_CHECK_TYPE_MISMATCH;
1816 for ( i = 0; i < NUM_HUD_GAUGES; i++ ) {
1817 if ( !SDL_strcasecmp(CTEXT(index), HUD_gauge_text[i]) )
1821 // if we reached the end of the list, then the name is invalid
1822 if ( i == NUM_HUD_GAUGES )
1823 return SEXP_CHECK_INVALID_GAUGE_NAME;
1827 case OPF_JUMP_NODE_NAME:
1828 if ( type2 != SEXP_ATOM_STRING )
1829 return SEXP_CHECK_TYPE_MISMATCH;
1831 for ( i = 0; i < Num_jump_nodes; i++ ) {
1832 if ( !SDL_strcasecmp(Jump_nodes[i].name, CTEXT(index)) )
1836 if ( i == Num_jump_nodes )
1837 return SEXP_CHECK_INVALID_JUMP_NODE;
1842 case OPF_VARIABLE_NAME:
1843 if ( Fred_running ) {
1844 if ( get_index_sexp_variable_name(Sexp_nodes[index].text) == -1) {
1845 return SEXP_CHECK_INVALID_VARIABLE;
1848 // if Fred not running anything goes
1852 // type checking for modify-variable
1853 // string or number -- anything goes
1857 Int3(); // currently unhandled argument format (so add it now)
1860 index = Sexp_nodes[index].rest;
1867 // Stuff a string (" chars ") in *str, return length.
1869 int get_string(char *str)
1873 len = strcspn(Mp + 1, "\"");
1874 SDL_strlcpy(str, Mp + 1, len+1);
1881 // get text to stuff into Sexp_node in case of variable
1882 // if Fred_running - stuff Sexp_variables[].variable_name
1883 // otherwise - stuff index into Sexp_variables array.
1884 void get_sexp_text_for_variable(char *text, char *token)
1889 // get variable name (up to '['
1890 end_index = strcspn(token, "[");
1891 SDL_assert( (end_index != 0) && (end_index < TOKEN_LENGTH-1) );
1892 SDL_strlcpy(text, token, end_index+1);
1894 if ( !Fred_running ) {
1895 // freespace - get index into Sexp_variables array
1896 sexp_var_index = get_index_sexp_variable_name(text);
1897 SDL_assert(sexp_var_index != -1);
1898 SDL_snprintf(text, TOKEN_LENGTH, "%d", sexp_var_index);
1903 // returns the first sexp index of data this function allocates. (start of this sexp)
1904 // recursive function - always sets first and then rest
1905 int get_sexp(char *token)
1907 int start, node, last, len, op, count;
1908 char variable_text[TOKEN_LENGTH];
1910 // start - the node allocated in first instance of fuction
1911 // node - the node allocated in current instance of function
1912 // count - number of nodes allocated this instance of function [do we set last.rest or first]
1913 // variable - whether string or number is a variable referencing Sexp_variables
1919 ignore_white_space();
1920 while (*Mp != ')') {
1921 SDL_assert(*Mp != EOF_CHAR);
1925 node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, get_sexp(token), -1);
1927 } else if (*Mp == '\"') {
1929 len = strcspn(Mp + 1, "\"");
1931 SDL_assert(Mp[len + 1] == '\"'); // hit EOF first (unterminated string)
1932 SDL_assert_release(len < TOKEN_LENGTH); // token is too long.
1934 // check if string variable
1935 if ( *(Mp + 1) == SEXP_VARIABLE_CHAR ) {
1937 // reduce length by 1 for end \"
1938 int length = len - 1;
1939 SDL_assert_release(length >= 1);
1940 // SDL_assert(length < 2*TOKEN_LENGTH+2);
1942 // start copying after skipping 1st char
1943 SDL_strlcpy(token, Mp + 2, length+1);
1945 get_sexp_text_for_variable(variable_text, token);
1946 node = alloc_sexp(variable_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1);
1948 SDL_strlcpy(token, Mp + 1, len+1);
1950 node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
1953 // bump past closing \" by 1 char
1957 // Sexp operator or number
1959 bool variable = false;
1960 while (*Mp != ')' && !is_white_space(*Mp)) {
1961 if ( (len == 0) && (*Mp == SEXP_VARIABLE_CHAR) ) {
1966 SDL_assert(*Mp != EOF_CHAR);
1967 SDL_assert(len < TOKEN_LENGTH - 1);
1968 token[len++] = *Mp++;
1973 op = identify_operator(token);
1975 node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
1978 // convert token text for variable
1979 get_sexp_text_for_variable(variable_text, token);
1981 node = alloc_sexp(variable_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1);
1983 node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1);
1990 SDL_assert(last != -1);
1991 Sexp_nodes[last].rest = node;
1996 SDL_assert(node != -1); // ran out of nodes. Time to raise the MAX!
1998 ignore_white_space();
2001 Mp++; // skip past the ')'
2006 // Stuffs a list of sexp variables
2007 int stuff_sexp_variable_list()
2010 char var_name[TOKEN_LENGTH];
2011 char default_value[TOKEN_LENGTH];
2012 char str_type[TOKEN_LENGTH];
2017 required_string("$Variables:");
2018 ignore_white_space();
2020 // check for start of list
2022 error_display(1, "Reading sexp variable list. Found [%c]. Expecting '('.\n", *Mp);
2023 throw PARSE_ERROR_INT_LIST;
2027 ignore_white_space();
2029 while (*Mp != ')') {
2030 SDL_assert(count < MAX_SEXP_VARIABLES);
2032 // get index - for debug
2034 ignore_gray_space();
2037 get_string(var_name);
2038 ignore_gray_space();
2040 // get default_value;
2041 get_string(default_value);
2042 ignore_gray_space();
2045 get_string(str_type);
2046 ignore_white_space();
2049 if (!SDL_strcasecmp(str_type, "number")) {
2050 type = SEXP_VARIABLE_NUMBER;
2051 } else if (!SDL_strcasecmp(str_type, "string")) {
2052 type = SEXP_VARIABLE_STRING;
2053 } else if (!SDL_strcasecmp(str_type, "block")) {
2054 type = SEXP_VARIABLE_BLOCK | SEXP_VARIABLE_BLOCK_EXP;
2056 type = SEXP_VARIABLE_UNKNOWN;
2062 // check if variable name already exists
2063 if ( (type == SEXP_VARIABLE_NUMBER) || (type == SEXP_VARIABLE_STRING) ) {
2064 SDL_assert(get_index_sexp_variable_name(var_name) == -1);
2067 sexp_add_variable(default_value, var_name, type, index);
2076 void build_sexp_text_string(char *buffer, const int max_bufsize, int node, int mode)
2078 if (Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) {
2080 int sexp_variables_index = get_index_sexp_variable_name(Sexp_nodes[node].text);
2081 SDL_assert(sexp_variables_index != -1);
2082 SDL_assert( (Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER) || (Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING) );
2085 if (Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER) {
2086 SDL_assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER);
2088 // Error check - can be Fred or Freespace
2089 if (mode == SEXP_ERROR_CHECK_MODE) {
2090 if ( Fred_running ) {
2091 SDL_snprintf(buffer, max_bufsize, "%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
2093 SDL_snprintf(buffer, max_bufsize, "%s[%s] ", Sexp_variables[sexp_variables_index].variable_name, Sexp_variables[sexp_variables_index].text);
2096 // Save as string - only Fred
2097 SDL_assert(mode == SEXP_SAVE_MODE);
2098 SDL_snprintf(buffer, max_bufsize, "@%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
2102 SDL_assert(Sexp_nodes[node].subtype == SEXP_ATOM_STRING);
2103 SDL_assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING);
2105 // Error check - can be Fred or Freespace
2106 if (mode == SEXP_ERROR_CHECK_MODE) {
2107 if ( Fred_running ) {
2108 SDL_snprintf(buffer, max_bufsize, "%s[%s] ", Sexp_variables[sexp_variables_index].variable_name, Sexp_variables[sexp_variables_index].text);
2110 SDL_snprintf(buffer, max_bufsize, "%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
2113 // Save as string - only Fred
2114 SDL_assert(mode == SEXP_SAVE_MODE);
2115 SDL_snprintf(buffer, max_bufsize, "\"@%s[%s]\" ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
2120 if (Sexp_nodes[node].subtype == SEXP_ATOM_STRING) {
2121 SDL_snprintf(buffer, max_bufsize, "\"%s\" ", CTEXT(node));
2123 SDL_snprintf(buffer, max_bufsize, "%s ", CTEXT(node));
2130 int build_sexp_string(int cur_node, int level, int mode)
2133 int len, offset, node;
2135 Sexp_build_flag = 0;
2136 offset = strlen(Sexp_string);
2137 SDL_strlcat(Sexp_string, "( ", Sexp_string_len);
2139 while (node != -1) {
2140 SDL_assert(node >= 0 && node < MAX_SEXP_NODES);
2141 if (Sexp_nodes[node].first == -1) {
2142 // build text to string
2143 build_sexp_text_string(pstr, SDL_arraysize(pstr), node, mode);
2144 SDL_strlcat(Sexp_string, pstr, Sexp_string_len);
2147 build_sexp_string(Sexp_nodes[node].first, level + 1, mode);
2150 node = Sexp_nodes[node].rest;
2153 SDL_strlcat(Sexp_string, ") ", Sexp_string_len);
2154 len = strlen(Sexp_string) - offset;
2156 Sexp_string[offset] = 0;
2157 build_extended_sexp_string(cur_node, level, mode);
2164 void build_extended_sexp_string(int cur_node, int level, int mode)
2167 int i, flag = 0, node;
2169 SDL_strlcat(Sexp_string, "( ", Sexp_string_len);
2171 while (node != -1) {
2172 if (flag) // not the first line?
2173 for (i=0; i<level + 1; i++)
2174 SDL_strlcat(Sexp_string, " ", Sexp_string_len);
2177 SDL_assert(node >= 0 && node < MAX_SEXP_NODES);
2178 if (Sexp_nodes[node].first == -1) {
2179 build_sexp_text_string(pstr, SDL_arraysize(pstr),node, mode);
2180 SDL_strlcat(Sexp_string, pstr, Sexp_string_len);
2183 build_sexp_string(Sexp_nodes[node].first, level + 1, mode);
2186 SDL_strlcat(Sexp_string, "\n", Sexp_string_len);
2187 node = Sexp_nodes[node].rest;
2190 for (i=0; i<level; i++)
2191 SDL_strlcat(Sexp_string, " ", Sexp_string_len);
2193 SDL_strlcat(Sexp_string, ")", Sexp_string_len);
2196 void convert_sexp_to_string(int cur_node, char *outstr, const int outstr_len, int mode)
2198 Sexp_string = outstr;
2199 Sexp_string_len = outstr_len;
2202 build_sexp_string(cur_node, 0, mode);
2204 SDL_strlcpy(Sexp_string, "( )", Sexp_string_len);
2207 // determine if the named ship or wing hasn't arrived yet (wing or ship must be on arrival list)
2208 int sexp_query_has_yet_to_arrive(char *name)
2212 if (ship_query_state(name) < 0)
2215 i = wing_name_lookup(name, 1);
2217 // has not arrived yet, and never will arrive
2218 if ((i >= 0) && (Wings[i].num_waves >= 0) && (Wings[i].flags & WF_NEVER_EXISTED)){
2222 // has not arrived yet
2223 if ((i >= 0) && (Wings[i].num_waves >= 0) && !Wings[i].total_arrived_count){
2230 // arithmetic functions
2231 int add_sexps(int n)
2237 sum = eval_sexp( CAR(n) );
2239 sum = atoi( CTEXT(n) );
2241 // be sure to check for the NAN value when doing arithmetic -- this value should
2242 // get propagated to the next highest function.
2243 if ( Sexp_nodes[CAR(n)].value == SEXP_NAN )
2245 else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER )
2246 return SEXP_NAN_FOREVER;
2248 while (CDR(n) != -1) {
2249 val = eval_sexp( CDR(n) );
2250 // be sure to check for the NAN value when doing arithmetic -- this value should
2251 // get propagated to the next highest function.
2252 if ( Sexp_nodes[CDR(n)].value == SEXP_NAN )
2254 else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER )
2255 return SEXP_NAN_FOREVER;
2264 int sub_sexps(int n)
2269 if (Sexp_nodes[n].first != -1)
2270 sum = eval_sexp(CAR(n));
2272 sum = atoi(CTEXT(n));
2274 while (CDR(n) != -1) {
2275 sum -= eval_sexp(CDR(n));
2283 int mul_sexps(int n)
2288 if (Sexp_nodes[n].first != -1)
2289 sum = eval_sexp(Sexp_nodes[n].first);
2291 sum = atoi(CTEXT(n));
2293 while (Sexp_nodes[n].rest != -1) {
2294 sum *= eval_sexp(Sexp_nodes[n].rest);
2295 n = Sexp_nodes[n].rest;
2302 int div_sexps(int n)
2307 if (Sexp_nodes[n].first != -1)
2308 sum = eval_sexp(Sexp_nodes[n].first);
2310 sum = atoi(CTEXT(n));
2312 while (Sexp_nodes[n].rest != -1) {
2313 sum /= eval_sexp(Sexp_nodes[n].rest);
2314 n = Sexp_nodes[n].rest;
2321 int mod_sexps(int n)
2326 if (Sexp_nodes[n].first != -1)
2327 sum = eval_sexp(Sexp_nodes[n].first);
2329 sum = atoi(CTEXT(n));
2331 while (Sexp_nodes[n].rest != -1) {
2332 sum = sum % eval_sexp(Sexp_nodes[n].rest);
2333 n = Sexp_nodes[n].rest;
2340 int rand_internal(int low, int high)
2344 // get diff - don't allow negative or zero
2350 return (low + myrand() % (diff + 1));
2354 int rand_sexp(int n, int multiple=0)
2361 if (Sexp_nodes[n].value == SEXP_NUM_EVAL) {
2362 // don't regenerate new random number
2363 rand_num = atoi(CTEXT(n));
2366 // if (Sexp_nodes[n].first != -1) {
2367 // low = eval_sexp(Sexp_nodes[n].first);
2369 // low = atoi(CTEXT(n));
2374 high = num_eval(CDR(n));
2376 // get the random number
2377 rand_num = rand_internal(low, high);
2380 // set .value and .text so random number is generated only once.
2381 Sexp_nodes[n].value = SEXP_NUM_EVAL;
2382 SDL_snprintf(Sexp_nodes[n].text, TOKEN_LENGTH, "%d", rand_num);
2390 // boolean evaluation functions. Evaluate all sexpressions in the 'or' operator. Needed to mark
2391 // entries in the mission log as essential so when pruning the log, we know which entries we might
2395 int all_false, result;
2400 if (Sexp_nodes[n].first != -1) {
2401 result |= eval_sexp(Sexp_nodes[n].first);
2402 if ( Sexp_nodes[Sexp_nodes[n].first].value == SEXP_KNOWN_TRUE )
2403 return SEXP_KNOWN_TRUE; // if one of the OR clauses is TRUE, whole clause is true
2404 if ( Sexp_nodes[Sexp_nodes[n].first].value != SEXP_KNOWN_FALSE ) // if the value is still unknown, they all can't be false
2407 result |= atoi(CTEXT(n));
2409 // don't return on true value -- keep evaluating for mission log purposes
2413 while (Sexp_nodes[n].rest != -1) {
2414 result |= eval_sexp(Sexp_nodes[n].rest);
2415 if ( Sexp_nodes[Sexp_nodes[n].rest].value == SEXP_KNOWN_TRUE )
2416 return SEXP_KNOWN_TRUE; // if one of the OR clauses is TRUE, whole clause is true
2417 if ( Sexp_nodes[Sexp_nodes[n].rest].value != SEXP_KNOWN_FALSE ) // if the value is still unknown, they all can't be false
2419 n = Sexp_nodes[n].rest;
2420 // don't return on true value -- keep evaluating for mission log purposes
2427 return SEXP_KNOWN_FALSE;
2432 // this function does the 'and' operator. It will short circuit evaluation *but* it will still
2433 // evaluate other members of the and construct. I do this because I need events in the mission log
2434 // to get marked as essential for goal purposes, and evaluation is pretty much the only way
2437 int all_true, result;
2442 if (Sexp_nodes[n].first != -1) {
2443 result &= eval_sexp( CAR(n) );
2444 if ( Sexp_nodes[Sexp_nodes[n].first].value == SEXP_KNOWN_FALSE )
2445 return SEXP_KNOWN_FALSE; // if one of the AND clauses is FALSE, whole clause is false
2446 if ( Sexp_nodes[Sexp_nodes[n].first].value != SEXP_KNOWN_TRUE ) // if the value is still unknown, they all can't be true
2449 result &= atoi(CTEXT(n));
2451 // don't short circuit -- evaluate everything for purposes of marking mission log
2455 while (Sexp_nodes[n].rest != -1) {
2458 new_result = eval_sexp( CDR(n) );
2459 result &= new_result;
2460 if ( Sexp_nodes[Sexp_nodes[n].rest].value == SEXP_KNOWN_FALSE )
2461 return SEXP_KNOWN_FALSE; // if one of the OR clauses is TRUE, whole clause is true
2462 if ( Sexp_nodes[Sexp_nodes[n].rest].value != SEXP_KNOWN_TRUE ) // if the value is still unknown, they all can't be false
2464 // don't short circuit -- evaluate everything for purposes of marking mission log
2468 n = Sexp_nodes[n].rest;
2473 return SEXP_KNOWN_TRUE;
2478 // this version of the 'and' operator determines whether or not it's arguments become true
2479 // in the order in which they are specified in the when statement. Should be a simple matter of
2480 // seeing if anything evaluates to true later than something that evalueated to false
2481 int sexp_and_in_sequence(int n)
2486 all_true = 1; // represents whether or not all nodes we have seen so far are true
2488 if (Sexp_nodes[n].first != -1) {
2489 result &= eval_sexp( CAR(n) );
2490 if ( Sexp_nodes[Sexp_nodes[n].first].value == SEXP_KNOWN_FALSE )
2491 return SEXP_KNOWN_FALSE; // if one of the AND clauses is FALSE, whole clause is false
2492 if ( Sexp_nodes[Sexp_nodes[n].first].value != SEXP_KNOWN_TRUE ) // if value is true, mark our all_true variable for later checking
2495 result &= atoi(CTEXT(n));
2497 // a little test -- if the previous sexpressions was true, then mark the node itself as always
2498 // true. I did this because of the distance function. It might become true, then when waiting for
2499 // the second evalation, it might become false, rendering this function false. So, when one becomes
2500 // true -- mark it true forever.
2502 Sexp_nodes[Sexp_nodes[n].first].value = SEXP_KNOWN_TRUE;
2504 while (Sexp_nodes[n].rest != -1) {
2507 next_result = eval_sexp( CDR(n) );
2508 if ( next_result && !result ) // if current result is true, and our running result is false, thngs didn't become true in order
2509 return SEXP_KNOWN_FALSE;
2510 result &= next_result;
2511 if ( Sexp_nodes[Sexp_nodes[n].rest].value == SEXP_KNOWN_FALSE )
2512 return SEXP_KNOWN_FALSE; // if one of the OR clauses is TRUE, whole clause is true
2513 if ( Sexp_nodes[Sexp_nodes[n].rest].value != SEXP_KNOWN_TRUE ) // if the value is still unknown, they all can't be false
2515 // see comment above for explanation of next lines
2517 Sexp_nodes[Sexp_nodes[n].rest].value = SEXP_KNOWN_TRUE;
2518 n = Sexp_nodes[n].rest;
2523 return SEXP_KNOWN_TRUE;
2528 // for these four basic boolean operations (not, <, >, and =), we have special cases that we must deal
2529 // with. We have sexpressions operators that might return a NAN type return value (such as the distance
2530 // between two ships when one of the ships is destroyed or departed). These operations need to check for
2531 // this special NAN value and adjust their return types accordingly. NAN values represent false return values
2532 int sexp_not( int n )
2537 if (Sexp_nodes[n].first != -1) {
2538 result = eval_sexp( CAR(n) );
2539 if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_FALSE )
2540 return SEXP_KNOWN_TRUE; // not KNOWN_FALSE == KNOWN_TRUE;
2541 else if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_TRUE ) // not KNOWN_TRUE == KNOWN_FALSE
2542 return SEXP_KNOWN_FALSE;
2543 else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN ) // not NAN == TRUE (I think)
2545 else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER )
2548 result = atoi(CTEXT(n));
2558 exp1 = eval_sexp( n );
2559 exp2 = eval_sexp( CDR(n) );
2561 // check for the NAN value
2562 if ( ( Sexp_nodes[CAR(n)].value == SEXP_NAN ) || ( Sexp_nodes[CDR(n)].value == SEXP_NAN ) )
2564 else if ( ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER ) || ( Sexp_nodes[CDR(n)].value == SEXP_NAN_FOREVER ) )
2565 return SEXP_KNOWN_FALSE;
2576 exp1 = eval_sexp( n );
2577 exp2 = eval_sexp( CDR(n) );
2579 // check for the NAN value
2580 if ( ( Sexp_nodes[CAR(n)].value == SEXP_NAN ) || ( Sexp_nodes[CDR(n)].value == SEXP_NAN ) )
2582 else if ( ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER ) || ( Sexp_nodes[CDR(n)].value == SEXP_NAN_FOREVER ) )
2583 return SEXP_KNOWN_FALSE;
2590 int sexp_equal(int n)
2594 exp1 = eval_sexp( n );
2595 exp2 = eval_sexp( CDR(n) );
2597 // check for the NAN value
2598 if ( ( Sexp_nodes[CAR(n)].value == SEXP_NAN ) || ( Sexp_nodes[CDR(n)].value == SEXP_NAN ) )
2600 else if ( ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER ) || ( Sexp_nodes[CDR(n)].value == SEXP_NAN_FOREVER ) )
2601 return SEXP_KNOWN_FALSE;
2608 // Evaluate if given ship is destroyed.
2609 // Return true if the ship in the expression has been destroyed.
2610 int sexp_is_destroyed(int n, fix *latest_time)
2613 int count, num_destroyed, wing_index;
2616 SDL_assert ( n != -1 );
2625 if (sexp_query_has_yet_to_arrive(name))
2626 return SEXP_CANT_EVAL;
2628 // check to see if this ship/wing has departed. If so, then function is known false
2629 if ( mission_log_get_time (LOG_SHIP_DEPART, name, NULL, NULL) || mission_log_get_time (LOG_WING_DEPART, name, NULL, NULL) )
2630 return SEXP_KNOWN_FALSE;
2632 // check the mission log. If ship/wing not destroyed, immediately return 0.
2633 if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, &time) || mission_log_get_time(LOG_WING_DESTROYED, name, NULL, &time) || mission_log_get_time(LOG_SELF_DESTRUCT, name, NULL, &time)) {
2635 if ( latest_time && (time > *latest_time) )
2636 *latest_time = time;
2638 // ship or wing isn't destroyed -- add to directive count
2639 if ( (wing_index = wing_name_lookup( name, 1 )) >= 0 ) {
2640 Directive_count += Wings[wing_index].current_count;
2645 // move to next ship/wing in list
2649 // special case to mark a directive for destroy wing objectives true after a short amount
2650 // of time when there are more waves for this wing.
2651 if ( (count == 1) && (wing_index >= 0) && (Directive_count == 0) ) {
2652 if ( Wings[wing_index].current_wave < Wings[wing_index].num_waves )
2653 Directive_count = DIRECTIVE_WING_ZERO;
2656 if ( count == num_destroyed )
2657 return SEXP_KNOWN_TRUE;
2663 // return true if the subsystem of the given ship has been destroyed or not
2664 int sexp_is_subsystem_destroyed(int n)
2666 char *ship_name, *subsys_name;
2668 SDL_assert( n != -1 );
2670 ship_name = CTEXT(n);
2671 subsys_name = CTEXT(CDR(n));
2673 if (sexp_query_has_yet_to_arrive(ship_name))
2674 return SEXP_CANT_EVAL;
2676 // if the ship has departed, no way to destroy it's subsystem.
2677 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL ))
2678 return SEXP_KNOWN_FALSE;
2680 if ( mission_log_get_time(LOG_SHIP_SUBSYS_DESTROYED, ship_name, subsys_name, NULL) )
2681 return SEXP_KNOWN_TRUE;
2687 // determines if a ship has docked
2688 int sexp_has_docked(int n)
2690 char *docker = CTEXT(n);
2691 char *dockee = CTEXT(CDR(n));
2692 int count = atoi(CTEXT(CDR(CDR(n)))); // count of times that we should look for
2694 if (sexp_query_has_yet_to_arrive(docker))
2695 return SEXP_CANT_EVAL;
2697 if (sexp_query_has_yet_to_arrive(dockee))
2698 return SEXP_CANT_EVAL;
2700 SDL_assert ( count > 0 );
2701 if ( mission_log_get_time(LOG_SHIP_DESTROYED, docker, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, dockee, NULL, NULL) )
2702 return SEXP_KNOWN_FALSE;
2704 if ( !mission_log_get_time_indexed(LOG_SHIP_DOCK, docker, dockee, count, NULL) )
2707 return SEXP_KNOWN_TRUE;
2710 // determines if a ship has undocked
2711 int sexp_has_undocked(int n)
2713 char *docker = CTEXT(n);
2714 char *dockee = CTEXT(CDR(n));
2715 int count = atoi(CTEXT(CDR(CDR(n))));
2717 if (sexp_query_has_yet_to_arrive(docker))
2718 return SEXP_CANT_EVAL;
2720 if (sexp_query_has_yet_to_arrive(dockee))
2721 return SEXP_CANT_EVAL;
2723 SDL_assert ( count > 0 );
2724 if ( !mission_log_get_time_indexed(LOG_SHIP_UNDOCK, docker, dockee, count, NULL) ) {
2725 // if either ship destroyed before they dock, then sexp is known false
2726 if ( mission_log_get_time(LOG_SHIP_DESTROYED, docker, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, dockee, NULL, NULL) )
2727 return SEXP_KNOWN_FALSE;
2732 return SEXP_KNOWN_TRUE;
2735 // determines if a ship has arrived onto the scene
2736 int sexp_has_arrived(int n, fix *latest_time)
2739 int count, num_arrived;
2747 // if there is no log entry for this ship/wing for arrival, sexpression is false
2748 if ( mission_log_get_time(LOG_SHIP_ARRIVE, name, NULL, &time) || mission_log_get_time(LOG_WING_ARRIVE, name, NULL, &time) ) {
2750 if ( latest_time && (time > *latest_time) )
2751 *latest_time = time;
2756 if ( count == num_arrived )
2757 return SEXP_KNOWN_TRUE;
2762 // determines if a ship/wing has departed
2763 int sexp_has_departed(int n, fix *latest_time)
2766 int count, num_departed;
2775 if (sexp_query_has_yet_to_arrive(name))
2776 return SEXP_CANT_EVAL;
2778 // if ship/wing destroyed, sexpression is known false. Also, if there is no departure log entry, then
2779 // the sexpression is not true.
2780 if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) || mission_log_get_time(LOG_WING_DESTROYED, name, NULL, NULL))
2781 return SEXP_KNOWN_FALSE;
2782 else if ( mission_log_get_time(LOG_SHIP_DEPART, name, NULL, &time) || mission_log_get_time(LOG_WING_DEPART, name, NULL, &time) ) {
2784 if ( latest_time && (time > *latest_time) )
2785 *latest_time = time;
2790 if ( count == num_departed )
2791 return SEXP_KNOWN_TRUE;
2796 // determines if ships are disabled
2797 int sexp_is_disabled( int n, fix *latest_time )
2800 int count, num_disabled;
2809 if (sexp_query_has_yet_to_arrive(name))
2810 return SEXP_CANT_EVAL;
2812 // if ship/wing destroyed, sexpression is known false. Also, if there is no disable log entry, then
2813 // the sexpression is not true.
2814 if ( mission_log_get_time(LOG_SHIP_DEPART, name, NULL, &time) || mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, &time) )
2815 return SEXP_KNOWN_FALSE;
2816 else if ( mission_log_get_time(LOG_SHIP_DISABLED, name, NULL, &time) ) {
2818 if ( latest_time && (time > *latest_time) )
2819 *latest_time = time;
2824 if ( count == num_disabled )
2825 return SEXP_KNOWN_TRUE;
2830 // function to determine if a ship is done flying waypoints
2831 int sexp_are_waypoints_done( int n )
2833 char *ship_name, *waypoint_name;
2835 ship_name = CTEXT(n);
2836 waypoint_name = CTEXT(CDR(n));
2838 if (sexp_query_has_yet_to_arrive(ship_name))
2839 return SEXP_CANT_EVAL;
2841 // a destroyed or departed ship will never reach their goal -- return known false
2842 if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
2843 return SEXP_KNOWN_FALSE;
2844 else if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) )
2845 return SEXP_KNOWN_FALSE;
2847 // now check the log for the waypoints done entry
2848 if ( mission_log_get_time(LOG_WAYPOINTS_DONE, ship_name, waypoint_name, NULL) )
2849 return SEXP_KNOWN_TRUE;
2855 // determines if ships are disarmed
2856 int sexp_is_disarmed( int n, fix *latest_time )
2859 int count, num_disarmed;
2868 if (sexp_query_has_yet_to_arrive(name))
2869 return SEXP_CANT_EVAL;
2871 // if ship/wing destroyed, sexpression is known false. Also, if there is no disarm log entry, then
2872 // the sexpression is not true.
2873 if ( mission_log_get_time(LOG_SHIP_DEPART, name, NULL, &time) || mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, &time) )
2874 return SEXP_KNOWN_FALSE;
2875 else if ( mission_log_get_time(LOG_SHIP_DISARMED, name, NULL, &time) ) {
2877 if ( latest_time && (time > *latest_time) )
2878 *latest_time = time;
2883 if ( count == num_disarmed )
2884 return SEXP_KNOWN_TRUE;
2889 // the following functions are similar to the above objective functions but return true/false
2890 // if N seconds have elasped after the corresponding function is true.
2891 int sexp_is_destroyed_delay(int n)
2896 SDL_assert ( n >= 0 );
2900 delay = i2f(num_eval(n));
2902 // check value of is_destroyed function. KNOWN_FALSE should be returned immediately
2903 val = sexp_is_destroyed( CDR(n), &time );
2904 if ( val == SEXP_KNOWN_FALSE )
2907 if ( val == SEXP_CANT_EVAL )
2908 return SEXP_CANT_EVAL;
2912 if ( (Missiontime - time) >= delay )
2913 return SEXP_KNOWN_TRUE;
2919 int sexp_is_subsystem_destroyed_delay( int n )
2921 char *ship_name, *subsys_name;
2924 SDL_assert( n != -1 );
2926 ship_name = CTEXT(n);
2927 subsys_name = CTEXT(CDR(n));
2928 delay = i2f(atoi(CTEXT(CDR(CDR(n)))));
2930 if (sexp_query_has_yet_to_arrive(ship_name))
2931 return SEXP_CANT_EVAL;
2933 // if the ship has departed, no way to destroy it's subsystem.
2934 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL ))
2935 return SEXP_KNOWN_FALSE;
2937 if ( mission_log_get_time(LOG_SHIP_SUBSYS_DESTROYED, ship_name, subsys_name, &time) ) {
2938 if ( (Missiontime - time) >= delay )
2939 return SEXP_KNOWN_TRUE;
2945 int sexp_is_disabled_delay( int n )
2950 SDL_assert ( n >= 0 );
2953 delay = i2f(atoi(CTEXT(n)));
2955 // check value of is_disable for known false and return immediately if it is.
2956 val = sexp_is_disabled( CDR(n), &time );
2957 if ( val == SEXP_KNOWN_FALSE )
2960 if ( val == SEXP_CANT_EVAL )
2961 return SEXP_CANT_EVAL;
2964 if ( (Missiontime - time) >= delay )
2965 return SEXP_KNOWN_TRUE;
2971 int sexp_is_disarmed_delay( int n )
2976 SDL_assert ( n >= 0 );
2979 delay = i2f(atoi(CTEXT(n)));
2981 // check value of is_disarmed for a known false value and return that immediately if it is
2982 val = sexp_is_disarmed( CDR(n), &time );
2983 if ( val == SEXP_KNOWN_FALSE )
2986 if ( val == SEXP_CANT_EVAL )
2987 return SEXP_CANT_EVAL;
2990 if ( (Missiontime - time) >= delay )
2991 return SEXP_KNOWN_TRUE;
2997 int sexp_has_docked_delay( int n )
2999 char *docker = CTEXT(n);
3000 char *dockee = CTEXT(CDR(n));
3001 int count = atoi(CTEXT(CDR(CDR(n)))); // count of times that we should look for
3002 fix delay = i2f(atoi(CTEXT(CDR(CDR(CDR(n))))));
3005 SDL_assert ( count > 0 );
3006 if ( mission_log_get_time(LOG_SHIP_DESTROYED, docker, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, dockee, NULL, NULL) )
3007 return SEXP_KNOWN_FALSE;
3009 if (sexp_query_has_yet_to_arrive(docker))
3010 return SEXP_CANT_EVAL;
3012 if (sexp_query_has_yet_to_arrive(dockee))
3013 return SEXP_CANT_EVAL;
3015 if ( !mission_log_get_time_indexed(LOG_SHIP_DOCK, docker, dockee, count, &time) )
3018 if ( (Missiontime - time) >= delay )
3019 return SEXP_KNOWN_TRUE;
3024 int sexp_has_undocked_delay( int n )
3026 char *docker = CTEXT(n);
3027 char *dockee = CTEXT(CDR(n));
3028 int count = atoi(CTEXT(CDR(CDR(n))));
3029 fix delay = i2f(atoi(CTEXT(CDR(CDR(CDR(n))))));
3032 if (sexp_query_has_yet_to_arrive(docker))
3033 return SEXP_CANT_EVAL;
3035 if (sexp_query_has_yet_to_arrive(dockee))
3036 return SEXP_CANT_EVAL;
3038 SDL_assert ( count > 0 );
3039 if ( !mission_log_get_time_indexed(LOG_SHIP_UNDOCK, docker, dockee, count, &time) ) {
3040 // if either ship destroyed before they dock, then sexp is known false
3041 if ( mission_log_get_time(LOG_SHIP_DESTROYED, docker, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, dockee, NULL, NULL) )
3042 return SEXP_KNOWN_FALSE;
3047 if ( (Missiontime - time) >= delay )
3048 return SEXP_KNOWN_TRUE;
3053 int sexp_has_arrived_delay( int n )
3058 SDL_assert ( n >= 0 );
3061 delay = i2f(atoi(CTEXT(n)));
3063 // check return value from arrived function. if can never arrive, then return that value here as well
3064 val = sexp_has_arrived( CDR(n), &time );
3065 if ( val == SEXP_KNOWN_FALSE )
3068 if ( val == SEXP_CANT_EVAL )
3069 return SEXP_CANT_EVAL;
3072 if ( (Missiontime - time) >= delay )
3073 return SEXP_KNOWN_TRUE;
3079 int sexp_has_departed_delay( int n )
3084 SDL_assert ( n >= 0 );
3087 delay = i2f(atoi(CTEXT(n)));
3089 // must first check to see if the departed function could ever be true/false or is true or false.
3090 // if it can never be true, return that value
3091 val = sexp_has_departed( CDR(n), &time);
3092 if ( val == SEXP_KNOWN_FALSE )
3095 if ( val == SEXP_CANT_EVAL )
3096 return SEXP_CANT_EVAL;
3099 if ( (Missiontime - time) >= delay )
3100 return SEXP_KNOWN_TRUE;
3106 // function to determine if a ship is done flying waypoints after N seconds
3107 int sexp_are_waypoints_done_delay( int n )
3109 char *ship_name, *waypoint_name;
3112 ship_name = CTEXT(n);
3113 waypoint_name = CTEXT(CDR(n));
3114 delay = i2f(atoi(CTEXT(CDR(CDR(n)))));
3116 if (sexp_query_has_yet_to_arrive(ship_name))
3117 return SEXP_CANT_EVAL;
3119 // a destroyed or departed ship will never reach their goal -- return known false
3121 // Not checking the entries below. Ships which warp out after reaching their goal (or getting
3122 // destroyed after their goal), but after reaching their waypoints, may have this goal incorrectly
3125 // now check the log for the waypoints done entry
3126 if ( mission_log_get_time(LOG_WAYPOINTS_DONE, ship_name, waypoint_name, &time) ) {
3127 if ( (Missiontime - time) >= delay )
3128 return SEXP_KNOWN_TRUE;
3130 if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
3131 return SEXP_KNOWN_FALSE;
3132 else if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) )
3133 return SEXP_KNOWN_FALSE;
3139 // function to determine is all of a given ship type are destroyed
3140 int sexp_ship_type_destroyed( int n )
3145 percent = atoi(CTEXT(n));
3146 shiptype = CTEXT(CDR(n));
3148 for ( type = 0; type < MAX_SHIP_TYPE_COUNTS; type++ ) {
3149 if ( !SDL_strcasecmp( Ship_type_names[type], shiptype) )
3153 // bogus if we reach the end of this array!!!!
3154 if ( type == MAX_SHIP_TYPE_COUNTS ) {
3159 if ( Ship_counts[type].total == 0 )
3162 // determine if the percentage of killed/total is >= percentage given in the expression
3163 if ( (Ship_counts[type].killed * 100 / Ship_counts[type].total) >= percent)
3164 return SEXP_KNOWN_TRUE;
3170 // following are time based functions
3171 int sexp_has_time_elapsed(int n)
3173 int time = num_eval(n);
3175 if ( f2i(Missiontime) >= time )
3176 return SEXP_KNOWN_TRUE;
3181 // next function returns the time into the mission
3182 int sexp_mission_time()
3184 return f2i(Missiontime);
3187 // returns percent of length of distance to special warpout plane
3188 int sexp_special_warp_dist( int n)
3194 ship_name = CTEXT(n);
3196 // check to see if either ship was destroyed or departed. If so, then make this node known
3198 if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, ship_name, NULL, NULL) ) {
3199 return SEXP_NAN_FOREVER;
3203 shipnum = ship_name_lookup(ship_name);
3208 // check that ship has warpout_objnum
3209 if (Ships[shipnum].special_warp_objnum == -1) {
3213 SDL_assert( (Ships[shipnum].special_warp_objnum >= 0) && (Ships[shipnum].special_warp_objnum < MAX_OBJECTS));
3214 if ( (Ships[shipnum].special_warp_objnum < 0) && (Ships[shipnum].special_warp_objnum >= MAX_OBJECTS) ) {
3218 // check the special warpout device is valid
3220 object *ship_objp = &Objects[Ships[shipnum].objnum];
3221 object *warp_objp = &Objects[Ships[shipnum].special_warp_objnum];
3222 if (warp_objp->type == OBJ_SHIP) {
3223 if (Ship_info[Ships[warp_objp->instance].ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
3232 // check if within 45 degree half-angle cone of facing
3233 float dot = fl_abs(vm_vec_dotprod(&warp_objp->orient.v.fvec, &ship_objp->orient.v.fvec));
3240 float dist = fvi_ray_plane(&hit_pt, &warp_objp->pos, &warp_objp->orient.v.fvec, &ship_objp->pos, &ship_objp->orient.v.fvec, 0.0f);
3241 polymodel *pm = model_get(Ships[shipnum].modelnum);
3242 dist += pm->mins.xyz.z;
3244 // return as a percent of length
3245 return (int) (100.0f * dist / ship_get_length(&Ships[shipnum]));
3249 int sexp_time_destroyed(int n)
3253 if ( !mission_log_get_time( LOG_SHIP_DESTROYED, CTEXT(n), NULL, &time) ){ // returns 0 when not found
3260 int sexp_time_wing_destroyed(int n)
3264 if ( !mission_log_get_time( LOG_WING_DESTROYED, CTEXT(n), NULL, &time) ){
3271 int sexp_time_docked(int n)
3274 char *docker = CTEXT(n);
3275 char *dockee = CTEXT(CDR(n));
3276 int count = atoi(CTEXT(CDR(CDR(n))));
3278 SDL_assert ( count > 0 );
3279 if ( !mission_log_get_time_indexed(LOG_SHIP_DOCK, docker, dockee, count, &time) ){
3286 int sexp_time_undocked(int n)
3289 char *docker = CTEXT(n);
3290 char *dockee = CTEXT(CDR(n));
3291 int count = atoi(CTEXT(CDR(CDR(n))));
3293 SDL_assert ( count > 0 );
3294 if ( !mission_log_get_time_indexed(LOG_SHIP_UNDOCK, docker, dockee, count, &time) ){
3301 int sexp_time_ship_arrived(int n)
3305 SDL_assert( n != -1 );
3306 if ( !mission_log_get_time( LOG_SHIP_ARRIVE, CTEXT(n), NULL, &time ) ){
3313 int sexp_time_wing_arrived(int n)
3317 SDL_assert( n != -1 );
3318 if ( !mission_log_get_time( LOG_WING_ARRIVE, CTEXT(n), NULL, &time ) ){
3325 int sexp_time_ship_departed(int n)
3329 SDL_assert( n != -1 );
3330 if ( !mission_log_get_time( LOG_SHIP_DEPART, CTEXT(n), NULL, &time ) ){
3337 int sexp_time_wing_departed(int n)
3341 SDL_assert( n != -1 );
3342 if ( !mission_log_get_time( LOG_WING_DEPART, CTEXT(n), NULL, &time ) ){
3349 // function to return the remaining shields as a percentage of the given ship.
3350 int sexp_shields_left(int n)
3352 int shipnum, percent;
3355 shipname = CTEXT(n);
3357 // if ship is gone or departed, cannot ever evaluate properly. Return NAN_FOREVER
3358 if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, shipname, NULL, NULL) ){
3359 return SEXP_NAN_FOREVER;
3362 shipnum = ship_name_lookup( shipname );
3363 if ( shipnum == -1 ){ // hmm.. if true, must not have arrived yet
3367 // now return the amount of shields left as a percentage of the whole.
3368 percent = (int)(get_shield_strength(&Objects[Ships[shipnum].objnum]) / Ship_info[Ships[shipnum].ship_info_index].shields * 100.0f);
3372 // function to return the remaining hits left as a percentage of the whole. This hit amount counts for
3373 // all hits on the ship (hull + subsystems). Use hits_left_hull to find hull hits remaining.
3374 int sexp_hits_left(int n)
3376 int shipnum, percent;
3379 shipname = CTEXT(n);
3381 // if ship is gone or departed, cannot ever evaluate properly. Return NAN_FOREVER
3382 if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, shipname, NULL, NULL) ){
3383 return SEXP_NAN_FOREVER;
3386 shipnum = ship_name_lookup( shipname );
3387 if ( shipnum == -1 ){ // hmm.. if true, must not have arrived yet
3391 // now return the amount of hits left as a percentage of the whole. Subtract the percentage from 100
3392 // since we are working with total hit points taken, not total remaining.
3393 ship *shipp = &Ships[shipnum];
3394 ship_info *sip = &Ship_info[shipp->ship_info_index];
3395 object *objp = &Objects[shipp->objnum];
3396 percent = (int) (100.0f * objp->hull_strength / sip->initial_hull_strength);
3400 // is ship visible on radar
3401 // returns 0 - not visible
3402 // returns 1 - marginally targetable (jiggly on radar)
3403 // returns 2 - fully targetable
3404 int sexp_is_ship_visible(int n)
3408 int ship_is_visible = 0;
3410 // if multiplayer, bail
3411 if (Game_mode & GM_MULTIPLAYER) {
3412 return SEXP_NAN_FOREVER;
3415 shipname = CTEXT(n);
3417 // if ship is gone or departed, cannot ever evaluate properly. Return NAN_FOREVER
3418 if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, shipname, NULL, NULL) ){
3419 return SEXP_NAN_FOREVER;
3422 shipnum = ship_name_lookup( shipname );
3423 if ( shipnum == -1 ){ // hmm.. if true, must not have arrived yet
3427 // get ship's *radar* visiblity
3428 if (Player_ship != NULL) {
3429 if (ship_is_visible_by_team(shipnum, Player_ship->team)) {
3430 ship_is_visible = 2;
3434 // only check awacs level if ship is not visible by team
3435 if (Player_ship != NULL && !ship_is_visible) {
3436 float awacs_level = awacs_get_level(&Objects[Ships[shipnum].objnum], Player_ship);
3437 if (awacs_level >= 1.0f) {
3438 ship_is_visible = 2;
3439 } else if (awacs_level > 0) {
3440 ship_is_visible = 1;
3444 return ship_is_visible;
3447 // get multi team v team score
3448 // if not multi team v team return 0
3449 // if invalid team return 0
3450 int sexp_team_score(int node)
3453 if (Game_mode & GM_MULTIPLAYER) {
3454 if (Netgame.type_flags & NG_TYPE_TEAM) {
3456 int team = atoi(CTEXT(node));
3459 return Multi_team0_score;
3460 } else if (team == 2) {
3461 return Multi_team1_score;
3463 // invalid team index
3474 // function to return the remaining hits left on a subsystem as a percentage of thw whole.
3475 int sexp_hits_left_subsystem(int n)
3477 int shipnum, percent, type;
3481 shipname = CTEXT(n);
3483 // if ship is gone or departed, cannot ever evaluate properly. Return NAN_FOREVER
3484 if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, shipname, NULL, NULL) ){
3485 return SEXP_NAN_FOREVER;
3488 shipnum = ship_name_lookup( shipname );
3489 if ( shipnum == -1 ){ // hmm.. if true, must not have arrived yet
3493 subsys_name = CTEXT(CDR(n));
3494 type = ai_get_subsystem_type( subsys_name );
3495 if ( (type >= 0) && (type < SUBSYSTEM_MAX) ) {
3496 // return as a percentage the hits remaining on the subsystem as a whole (i.e. for 3 engines,
3497 // we are returning the sum of the hits on the 3 engines)
3498 if (type == SUBSYSTEM_UNKNOWN) {
3499 // find the ship subsystem by searching ship's subsys_list
3501 ss = GET_FIRST( &Ships[shipnum].subsys_list );
3502 while ( ss != END_OF_LIST( &Ships[shipnum].subsys_list ) ) {
3504 if ( !SDL_strcasecmp(ss->system_info->subobj_name, subsys_name)) {
3505 percent = (int) (ss->current_hits / ss->system_info->max_hits * 100.0f);
3509 ss = GET_NEXT( ss );
3511 // we reached end of ship subsys list without finding subsys_name
3515 percent = (int)(ship_get_subsystem_strength(&Ships[shipnum],type) * 100.0f);
3519 return SEXP_NAN; // if for some strange reason, the type field of the subsystem is bogus
3522 int sexp_determine_team(char *subj)
3526 if (!SDL_strcasecmp(subj, "<any friendly>")){
3527 team = TEAM_FRIENDLY;
3528 } else if (!SDL_strcasecmp(subj, "<any hostile>")){
3529 team = TEAM_HOSTILE;
3530 } else if (!SDL_strcasecmp(subj, "<any neutral>")){
3531 team = TEAM_NEUTRAL;
3532 } else if (!SDL_strcasecmp(subj, "<any unknown>")){
3533 team = TEAM_UNKNOWN;
3534 } else if (!SDL_strcasecmp(subj, "<any traitor>")){
3535 team = TEAM_TRAITOR;
3541 // returns the distance between two objects. If a wing is specificed as one (or both) or the arguments
3542 // to this function, we are looking for the closest distance
3543 int sexp_distance(int n)
3545 int i, team, obj, dist, dist_min = 0, inited = 0;
3546 char *sname1, *sname2;
3551 sname2 = CTEXT(CDR(n));
3553 // check to see if either ship was destroyed or departed. If so, then make this node known
3555 if ( mission_log_get_time(LOG_SHIP_DESTROYED, sname1, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, sname1, NULL, NULL) ||
3556 mission_log_get_time(LOG_SHIP_DESTROYED, sname2, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, sname2, NULL, NULL) )
3557 return SEXP_NAN_FOREVER;
3559 // one of the names might be the name of a wing. Check to see if the wing is detroyed or departed
3560 if ( mission_log_get_time(LOG_WING_DESTROYED, sname1, NULL, NULL) || mission_log_get_time( LOG_WING_DEPART, sname1, NULL, NULL) ||
3561 mission_log_get_time(LOG_WING_DESTROYED, sname2, NULL, NULL) || mission_log_get_time( LOG_WING_DEPART, sname2, NULL, NULL) )
3562 return SEXP_NAN_FOREVER;
3564 team = sexp_determine_team(sname1);
3565 if (team) { // we have a team type, so check all ships of that type
3566 so = GET_FIRST(&Ship_obj_list);
3567 while (so != END_OF_LIST(&Ship_obj_list)) {
3568 if (Ships[Objects[so->objnum].instance].team == team) {
3570 dist = sexp_distance2(obj, sname2);
3571 if (dist != SEXP_NAN) {
3572 if (!inited || (dist < dist_min)) {
3582 if (!inited) // no objects were checked
3588 // at this point, we must have a wing, ship or point for a subj
3589 obj = ship_name_lookup(sname1);
3591 return sexp_distance2(Ships[obj].objnum, sname2);
3593 // at this point, we must have a wing or point for a subj
3594 obj = waypoint_lookup(sname1);
3596 return sexp_distance2(obj, sname2);
3598 // at this point, we must have a wing for a subj
3599 obj = wing_name_lookup(sname1);
3601 return SEXP_NAN; // we apparently don't have anything legal
3603 wingp = &Wings[obj];
3604 for (i=0; i<wingp->current_count; i++) {
3605 obj = Ships[wingp->ship_index[i]].objnum;
3606 dist = sexp_distance2(obj, sname2);
3607 if (dist != SEXP_NAN) {
3608 if (!inited || (dist < dist_min)) {
3615 if (!inited) // no objects were checked
3621 // check distance between a given ship and a given subject (ship, wing, any <team>).
3622 int sexp_distance2(int obj1, char *subj)
3624 int i, team, obj2, dist, dist_min = 0, inited = 0;
3628 team = sexp_determine_team(subj);
3629 if (team) { // we have a team type, so check all ships of that type
3630 so = GET_FIRST(&Ship_obj_list);
3631 while (so != END_OF_LIST(&Ship_obj_list)) {
3632 if (Ships[Objects[so->objnum].instance].team == team) {
3634 dist = sexp_distance3(obj1, obj2);
3635 if (dist != SEXP_NAN) {
3636 if (!inited || (dist < dist_min)) {
3646 if (!inited) // no objects were checked
3652 // at this point, we must have a wing, ship or point for a subj
3653 obj2 = ship_name_lookup(subj);
3655 return sexp_distance3(obj1, Ships[obj2].objnum);
3657 // at this point, we must have a wing or point for a subj
3658 obj2 = waypoint_lookup(subj);
3660 return sexp_distance3(obj1, obj2);
3662 // at this point, we must have a wing for a subj
3663 obj2 = wing_name_lookup(subj);
3665 return SEXP_NAN; // we apparently don't have anything legal
3667 wingp = &Wings[obj2];
3668 for (i=0; i<wingp->current_count; i++) {
3669 obj2 = Ships[wingp->ship_index[i]].objnum;
3670 dist = sexp_distance3(obj1, obj2);
3671 if (dist != SEXP_NAN) {
3672 if (!inited || (dist < dist_min)) {
3679 if (!inited) // no objects were checked
3685 // check distance between two given objects
3686 int sexp_distance3(int obj1, int obj2)
3688 if ( (obj1 == -1) || (obj2 == -1) ) // if either object isn't present in the mission now
3689 return SEXP_NAN; // return a really small number
3691 if ( (Objects[obj1].type == OBJ_SHIP) && (Objects[obj2].type == OBJ_SHIP) ) {
3692 if (OBJ_INDEX(Player_obj) == obj1)
3693 return (int) hud_find_target_distance( &Objects[obj2], &Objects[obj1] );
3695 return (int) hud_find_target_distance( &Objects[obj1], &Objects[obj2] );
3698 return (int) vm_vec_dist_quick( &Objects[obj1].pos, &Objects[obj2].pos );
3702 // funciton to determine when the last meaningful order was given to one or more ships. Returns
3703 // true or false depending on whether or not a meaningful order was received
3704 int sexp_last_order_time( int n )
3711 time = i2f(atoi(CTEXT(n)));
3712 SDL_assert ( time >= 0 );
3717 instance = ship_name_lookup(name);
3718 if ( instance != -1 ) {
3719 aigp = Ai_info[Ships[instance].ai_index].goals;
3721 instance = wing_name_lookup(name);
3722 if ( instance == -1 ) // if we cannot find ship or wing, return 0
3724 aigp = Wings[instance].ai_goals;
3727 // with the ship, check the ai_goals structure for this ship and determine if there are any
3728 // orders which are < time seconds since current mission time
3729 for ( i = 0; i < MAX_AI_GOALS; i++ ) {
3732 mode = aigp->ai_mode;
3733 if ( (mode != AI_GOAL_NONE) && (mode != AI_GOAL_WARP) )
3734 if ( (aigp->time + time) > Missiontime )
3738 if ( i == MAX_AI_GOALS )
3747 // sexpression to return the number of players in the mission
3748 int sexp_num_players()
3754 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
3755 if ( (objp->type == OBJ_SHIP) && (objp->flags & OF_PLAYER_SHIP) )
3762 // expression to determine if the current skill level of the game is at least
3763 // the skill level given in the sexpression
3764 int sexp_skill_level_at_least( int n )
3769 level_name = CTEXT(n);
3770 for (i = 0; i < NUM_SKILL_LEVELS; i++ ) {
3771 if ( !SDL_strcasecmp(level_name, Skill_level_names(i, 0)) ) {
3772 if ( Game_skill_level >= i ){
3780 // return 0 if not found!!!
3784 int sexp_was_promotion_granted(int n)
3786 if (Player->flags & PLAYER_FLAGS_PROMOTED)
3792 int sexp_was_medal_granted(int n)
3798 if (Player->stats.m_medal_earned >= 0)
3804 medal_name = CTEXT(n);
3805 for (i=0; i<NUM_MEDALS; i++) {
3806 if (!SDL_strcasecmp(medal_name, Medals[i].name))
3810 if ( (i < NUM_MEDALS) && (Player->stats.m_medal_earned == i) )
3816 // function which returns true if the percentage of ships (and ships in wings) departed is at
3817 // least the percentage given. what determine if we should check destroyed or departed status
3818 int sexp_percent_ships_depart_destroy(int n, int what)
3824 percent = atoi(CTEXT(n));
3828 // iterate through the rest of the ships/wings in the list and tally the departures and the
3830 for ( n = CDR(n); n != -1; n = CDR(n) ) {
3835 wingnum = wing_name_lookup( name, 1 );
3836 if ( wingnum != -1 ) {
3837 // for wings, we can increment the total by the total number of ships that we expect for
3838 // this wing, and the departures by the number of departures stored for this wing
3839 total += (Wings[wingnum].wave_count * Wings[wingnum].num_waves);
3840 if ( what == OP_PERCENT_SHIPS_DEPARTED )
3841 count += Wings[wingnum].total_departed;
3842 else if ( what == OP_PERCENT_SHIPS_DESTROYED )
3843 count += Wings[wingnum].total_destroyed;
3845 Int3(); // this would be very bogus!
3847 // must be a ship, so increment the total by 1, then determine if this ship has departed
3849 if ( what == OP_PERCENT_SHIPS_DEPARTED ) {
3850 if ( mission_log_get_time(LOG_SHIP_DEPART, name, NULL, NULL) )
3852 } else if ( what == OP_PERCENT_SHIPS_DESTROYED ) {
3853 if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) )
3856 Int3(); // this would be very bogus as well.
3861 // now, look at the percentage
3862 if ( (total > 0) && (((count * 100) / total) >= percent) )
3863 return SEXP_KNOWN_TRUE;
3868 // function to tell is a list of ships has departed from within a radius of a given jump node.
3869 // returns true N seconds after the list of ships have departed
3870 int sexp_depart_node_delay( int n )
3872 int delay, count, num_departed;
3873 char *jump_node_name, *name;
3874 fix latest_time, this_time;
3876 delay = atoi( CTEXT(n) );
3878 jump_node_name = CTEXT(n);
3880 // iterate through the list of ships
3889 if (sexp_query_has_yet_to_arrive(name))
3890 return SEXP_CANT_EVAL;
3892 // if ship/wing destroyed, sexpression is known false. Also, if there is no departure log entry, then
3893 // the sexpression is not true.
3894 if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) )
3895 return SEXP_KNOWN_FALSE;
3896 else if ( mission_log_get_time(LOG_SHIP_DEPART, name, jump_node_name, &this_time) ) {
3898 if ( this_time > latest_time )
3899 latest_time = this_time;
3904 if ( (count == num_departed) && ((Missiontime - latest_time) >= delay) )
3905 return SEXP_KNOWN_TRUE;
3910 // sexpression which returns true when the listed ships/wings have all been destroyed or
3912 int sexp_destroyed_departed_delay( int n )
3915 fix delay, latest_time;
3919 delay = i2f(atoi(CTEXT(n)));
3922 count = 0; // number destroyed or departed
3923 total = 0; // total number of ships/wings to check
3932 // for wings, check the WF_GONE flag to see if there are no more ships in this wing to arrive.
3933 wingnum = wing_name_lookup(name, 1);
3934 if ( wingnum != -1 ) {
3935 if ( Wings[wingnum].flags & WF_WING_GONE ) {
3936 // be sure to get the latest time of one of these
3937 if ( Wings[wingnum].time_gone > latest_time ){
3938 time_gone = Wings[wingnum].time_gone;
3942 } else if ( mission_log_get_time(LOG_SHIP_DEPART, name, NULL, &time_gone) ) {
3944 } else if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, &time_gone) ) {
3948 // check our latest time
3949 if ( time_gone > latest_time ){
3950 latest_time = time_gone;
3956 if ( (count == total) && (Missiontime > (latest_time + delay)) )
3957 return SEXP_KNOWN_TRUE;
3962 int sexp_special_warpout_name( int node )
3964 int shipnum, knossos_num;
3965 char *ship_name, *knossos;
3967 ship_name = CTEXT(node);
3968 knossos = CTEXT(CDR(node));
3970 // check to see if either ship was destroyed or departed. If so, then make this node known
3972 if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, ship_name, NULL, NULL) ||
3973 mission_log_get_time(LOG_SHIP_DESTROYED, knossos, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, knossos, NULL, NULL) )
3974 return SEXP_NAN_FOREVER;
3977 shipnum = ship_name_lookup(ship_name);
3983 knossos_num = ship_name_lookup(knossos);
3984 if (knossos_num < 0) {
3988 // set special warpout objnum
3989 Ships[shipnum].special_warp_objnum = knossos_num;
3994 // function which determines if N seconds have elpased since all discovery of all cargo
3996 int sexp_is_cargo_known( int n, int check_delay )
3998 int count, shipnum, num_known, delay;
4001 SDL_assert ( n >= 0 );
4006 // get the delay value (if there is one)
4008 if ( check_delay ) {
4009 delay = atoi(CTEXT(n) );
4021 // see if we have already checked this entry
4022 if ( Sexp_nodes[n].value == SEXP_KNOWN_TRUE ) {
4029 // see if the ship has already exited the mission (either through departure or destruction). If so,
4030 // grab the status of whether the cargo is known from this list
4031 exited_index = ship_find_exited_ship_by_name( name );
4032 if (exited_index != -1 ) {
4033 if ( !(Ships_exited[exited_index].flags & SEF_CARGO_KNOWN) )
4034 return SEXP_KNOWN_FALSE;
4036 // check the delay of when we found out. We use the ship died time which isn't entirely accurate
4037 // but won't cause huge delays.
4038 time_known = Missiontime - Ships_exited[exited_index].time;
4039 if ( f2i(time_known) >= delay )
4043 // otherwise, ship should still be in the mission. If ship_name_lookup returns -1, then ship
4044 // is yet to arrive.
4045 shipnum = ship_name_lookup( name );
4046 if ( shipnum != -1 ) {
4047 if ( Ships[shipnum].flags & SF_CARGO_REVEALED ) {
4048 time_known = Missiontime - Ships[shipnum].time_cargo_revealed;
4049 if ( f2i(time_known) >= delay )
4056 // if cargo is known, mark our variable and this sexpression.
4059 Sexp_nodes[n].value = SEXP_KNOWN_TRUE;
4065 Directive_count += count - num_known;
4066 if ( count == num_known )
4067 return SEXP_KNOWN_TRUE;
4072 int sexp_has_been_tagged_delay(int n)
4074 int count, shipnum, num_known, delay;
4077 SDL_assert ( n >= 0 );
4082 // get the delay value
4083 delay = atoi(CTEXT(n) );
4095 // see if we have already checked this entry
4096 if ( Sexp_nodes[n].value == SEXP_KNOWN_TRUE ) {
4103 // see if the ship has already exited the mission (either through departure or destruction). If so,
4104 // grab the status of whether the cargo is known from this list
4105 exited_index = ship_find_exited_ship_by_name( name );
4106 if (exited_index != -1 ) {
4107 if ( !(Ships_exited[exited_index].flags & SEF_BEEN_TAGGED) )
4108 return SEXP_KNOWN_FALSE;
4110 // check the delay of when we found out. We use the ship died time which isn't entirely accurate
4111 // but won't cause huge delays.
4112 time_known = Missiontime - Ships_exited[exited_index].time;
4113 if ( f2i(time_known) >= delay )
4117 // otherwise, ship should still be in the mission. If ship_name_lookup returns -1, then ship
4118 // is yet to arrive.
4119 shipnum = ship_name_lookup( name );
4120 if ( shipnum != -1 ) {
4121 if ( Ships[shipnum].time_first_tagged != 0 ) {
4122 time_known = Missiontime - Ships[shipnum].time_first_tagged;
4123 if ( f2i(time_known) >= delay )
4130 // if cargo is known, mark our variable and this sexpression.
4133 Sexp_nodes[n].value = SEXP_KNOWN_TRUE;
4139 Directive_count += count - num_known;
4140 if ( count == num_known )
4141 return SEXP_KNOWN_TRUE;
4146 int sexp_cap_subsys_cargo_known_delay(int n)
4148 int delay, count, delta_time, num_known;
4149 char *ship_name, *subsys_name;
4155 delay = atoi(CTEXT(n));
4159 ship_name = CTEXT(n);
4170 // see if we have already checked this entry
4171 if ( Sexp_nodes[n].value == SEXP_KNOWN_TRUE ) {
4175 subsys_name = CTEXT(n);
4177 logged = mission_log_get_time(LOG_CAP_SUBSYS_CARGO_REVEALED, ship_name, subsys_name, &time_known);
4179 delta_time = f2i(Missiontime - time_known);
4180 if (delta_time >= delay) {
4185 // if (exited or destroyed) and not logged, known false
4186 // otherwise, still out there and cargo not yet known
4187 if ( (mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL ) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL )) && !logged ) {
4188 return SEXP_KNOWN_FALSE;
4194 Sexp_nodes[n].value = SEXP_KNOWN_TRUE;
4200 Directive_count += count - num_known;
4201 if ( count == num_known )
4202 return SEXP_KNOWN_TRUE;
4208 // return object index of waypoint or -1 if no such waypoint
4209 int waypoint_lookup(char *name)
4215 ptr = GET_FIRST(&obj_used_list);
4216 while (ptr != END_OF_LIST(&obj_used_list)) {
4217 if (ptr->type == OBJ_WAYPOINT) {
4219 SDL_snprintf(buf, SDL_arraysize(buf), "%s:%d", Waypoint_lists[i / 65536].name, (i & 0xffff) + 1);
4220 if ( !SDL_strcasecmp(buf, name) )
4221 return OBJ_INDEX(ptr);
4224 ptr = GET_NEXT(ptr);
4230 // conditional sexpressions follow
4232 // eval_when evaluates the when conditional
4233 int eval_when(int n)
4237 SDL_assert( n >= 0 ); // must have valid sexp index
4240 val = eval_sexp(cond); // get the value of the the conditional
4241 if ( val ) { // if the value is true, perform the actions is the 'then' part of the if
4245 while ( actions != -1 ) {
4248 val = eval_sexp(exp); // these sexp evaled only for side effects
4249 actions = CDR(actions);
4253 if (Sexp_nodes[cond].value == SEXP_KNOWN_FALSE)
4254 return SEXP_KNOWN_FALSE; // no need to waste time on this anymore
4256 if (val == SEXP_KNOWN_FALSE)
4257 return 0; // can't return known false, as this would bypass future actions under the when
4262 // eval_cond() evaluates the cond conditional
4263 int eval_cond( int n )
4265 int cond = 0, node, val = 0;
4267 SDL_assert (n >= 0);
4271 val = eval_sexp(cond);
4273 // if the conditional evaluated to true, then we must evaluate the rest of the expression returning
4274 // the value of this evaluation
4279 actions = CDR(node);
4280 while (actions >= 0) {
4283 val = eval_sexp(exp); // these sexp evaled only for side effects
4285 actions = CDR(actions);
4291 // move onto the next cond clause
4298 int sexp_is_iff( int n )
4300 char *ship_name, *iff;
4303 SDL_assert ( n >= 0 );
4305 // iff value is the first parameter, second is a list of one or more ships to check to see if the
4306 // iff value matches
4308 if ( !SDL_strcasecmp(iff, "friendly") )
4309 team = TEAM_FRIENDLY;
4310 else if ( !SDL_strcasecmp(iff, "hostile") )
4311 team = TEAM_HOSTILE;
4312 else if ( !SDL_strcasecmp(iff, "neutral") )
4313 team = TEAM_NEUTRAL;
4314 else if ( !SDL_strcasecmp(iff, "unknown") )
4315 team = TEAM_UNKNOWN;
4316 else if ( !SDL_strcasecmp(iff, "traitor") )
4317 team = TEAM_TRAITOR;
4320 mprintf(("Warning: Team %s no longer supported. Just Friendly and Hostile.\n", iff));
4321 team = TEAM_HOSTILE;
4326 ship_name = CTEXT(n);
4327 // find the ship and check to be sure that it is still around.
4328 num = ship_name_lookup(ship_name);
4329 if ( num < 0 ) // if the ship is gone, can't check it's iff.
4332 // if the team doesn't match the team specified, return 0 immediately
4333 if ( Ships[num].team != team)
4342 void sexp_change_iff( int n )
4344 char *ship_name, *new_iff;
4347 SDL_assert ( n >= 0 );
4349 if ( !SDL_strcasecmp(new_iff, "friendly") )
4350 new_team = TEAM_FRIENDLY;
4351 else if ( !SDL_strcasecmp(new_iff, "hostile") )
4352 new_team = TEAM_HOSTILE;
4353 else if ( !SDL_strcasecmp(new_iff, "neutral") )
4354 new_team = TEAM_NEUTRAL;
4355 else if ( !SDL_strcasecmp(new_iff, "unknown") )
4356 new_team = TEAM_UNKNOWN;
4357 else if ( !SDL_strcasecmp(new_iff, "traitor") )
4358 new_team = TEAM_TRAITOR;
4360 mprintf(("Warning: Team %s no longer supported. Just Friendly and Hostile.\n", new_iff));
4361 new_team = TEAM_HOSTILE;
4367 ship_name = CTEXT(n);
4369 // find the ship and check to be sure that it is still around.
4370 num = ship_name_lookup(ship_name);
4371 if ( num >= 0 ) { // only change iff if we found the ship
4372 Ships[num].team = new_team;
4374 // send a network packet if we need to
4375 if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Ships[num].objnum >= 0)){
4376 send_change_iff_packet(Objects[Ships[num].objnum].net_signature, new_team);
4385 // following routine adds an ai goal to a ship structure. The sexpression index
4386 // passed in should be an ai-goal of the proper form. The code in MissionGoal should
4387 // check the syntax.
4389 void sexp_add_ship_goal( int n )
4394 SDL_assert ( n >= 0 );
4395 ship_name = CTEXT(n);
4396 num = ship_name_lookup(ship_name);
4397 if ( num < 0 ) // ship not around anymore???? then forget it!
4401 ai_add_ship_goal_sexp( sindex, AIG_TYPE_EVENT_SHIP, &(Ai_info[Ships[num].ai_index]) );
4404 // identical to above, except add a wing
4405 void sexp_add_wing_goal( int n )
4410 SDL_assert ( n >= 0 );
4411 wing_name = CTEXT(n);
4412 num = wing_name_lookup(wing_name);
4413 if ( num < 0 ) // ship not around anymore???? then forget it!
4417 ai_add_wing_goal_sexp( sindex, AIG_TYPE_EVENT_WING, num );
4420 // sexp_add_goal adds a goal to the specified entiry (ships and wings have unique names between
4422 void sexp_add_goal( int n )
4427 SDL_assert ( n >= 0 );
4431 // first, look for ship name -- if found, then add ship goal. else look for wing name -- if
4432 // found, add wing goal
4433 if ( (num = ship_name_lookup(name)) != -1 )
4434 ai_add_ship_goal_sexp( sindex, AIG_TYPE_EVENT_SHIP, &(Ai_info[Ships[num].ai_index]) );
4435 else if ( (num = wing_name_lookup(name)) != -1 )
4436 ai_add_wing_goal_sexp( sindex, AIG_TYPE_EVENT_WING, num );
4439 // clears out all ai goals for a ship
4440 void sexp_clear_ship_goals( int n )
4445 SDL_assert ( n >= 0 );
4446 ship_name = CTEXT(n);
4447 num = ship_name_lookup(ship_name);
4448 ai_clear_ship_goals( &(Ai_info[Ships[num].ai_index]) );
4451 // clears out ai goals for a wing
4452 void sexp_clear_wing_goals( int n )
4457 SDL_assert ( n >= 0 );
4458 wing_name = CTEXT(n);
4459 num = wing_name_lookup(wing_name);
4462 ai_clear_wing_goals( num );
4465 // this function clears all ai goals for the given ship or wing
4466 void sexp_clear_goals( int n )
4471 SDL_assert ( n >= 0 );
4474 if ( (num = ship_name_lookup(name)) != -1 )
4475 ai_clear_ship_goals( &(Ai_info[Ships[num].ai_index]) );
4476 else if ( (num = wing_name_lookup(name)) != -1 )
4477 ai_clear_wing_goals( num );
4483 // this function get called by send-message or send-message random with the name of the message, sender,
4485 void sexp_send_one_message( char *name, char *who_from, char *priority, int group, int delay )
4487 int ipriority, num, ship_index, source;
4494 // determine the priority of the message
4495 if ( !SDL_strcasecmp(priority, "low") )
4496 ipriority = MESSAGE_PRIORITY_LOW;
4497 else if ( !SDL_strcasecmp(priority, "normal") )
4498 ipriority = MESSAGE_PRIORITY_NORMAL;
4499 else if ( !SDL_strcasecmp(priority, "high") )
4500 ipriority = MESSAGE_PRIORITY_HIGH;
4503 ipriority = MESSAGE_PRIORITY_NORMAL;
4506 // check to see if the 'who_from' string is a ship that had been destroyed or departed. If so,
4507 // then don't send the message. We must look at 'who_from' to determine what to look for. who_from
4508 // may be any allied person, any wingman, a wingman from a specific wing, or a specific ship
4511 source = MESSAGE_SOURCE_COMMAND;
4512 if ( who_from[0] == '#' ) {
4513 message_send_unique_to_player( name, &(who_from[1]), MESSAGE_SOURCE_SPECIAL, ipriority, group, delay );
4515 } else if (!SDL_strcasecmp(who_from, "<any allied>")) {
4516 //Int3(); // no longer supported
4518 } else if ( (num = wing_name_lookup(who_from)) != -1 ) {
4519 // message from a wing
4520 // this will be an invalid case soon
4522 // choose wing leader to speak for wing (hence "1" at end of ship_get_random_ship_in_wing)
4523 ship_index = ship_get_random_ship_in_wing( num, SHIP_GET_NO_PLAYERS, 1 );
4524 if ( ship_index == -1 ) {
4525 if ( ipriority != MESSAGE_PRIORITY_HIGH )
4529 } else if ( mission_log_get_time(LOG_SHIP_DESTROYED, who_from, NULL, NULL) || mission_log_get_time(LOG_SHIP_DEPART, who_from, NULL, NULL)
4530 || mission_log_get_time(LOG_WING_DESTROYED, who_from, NULL, NULL) || mission_log_get_time(LOG_WING_DEPART, who_from, NULL, NULL) ) {
4531 // getting into this if statement means that the ship or wing (sender) is no longer in the mission
4532 // if message is high priority, make it come from Terran Command
4533 if ( ipriority != MESSAGE_PRIORITY_HIGH )
4536 source = MESSAGE_SOURCE_COMMAND;
4538 } else if ( !SDL_strcasecmp(who_from, "<any wingman>") || (wing_name_lookup(who_from) != -1) ) {
4539 source = MESSAGE_SOURCE_WINGMAN;
4541 // Message from a apecific ship
4542 // bail if not high priority, otherwise reroute to command
4543 source = MESSAGE_SOURCE_SHIP;
4544 ship_index = ship_name_lookup(who_from);
4545 if ( ship_index == -1 ) {
4546 if ( ipriority != MESSAGE_PRIORITY_HIGH )
4548 source = MESSAGE_SOURCE_COMMAND;
4552 if ( ship_index == -1 ){
4555 shipp = &Ships[ship_index];
4558 message_send_unique_to_player( name, shipp, source, ipriority, group, delay );
4561 void sexp_send_message( int n )
4563 char *name, *who_from, *priority, *tmp;
4569 SDL_assert ( n != -1 );
4570 who_from = CTEXT(n);
4571 priority = CTEXT(CDR(n));
4572 name = CTEXT(CDR(CDR(n)));
4574 // a temporary check to see if the name field matched a priority since I am in the process
4575 // of reordering the arguments
4576 if ( !SDL_strcasecmp(name, "low") || !SDL_strcasecmp(name, "normal") || !SDL_strcasecmp(name, "high") ) {
4582 sexp_send_one_message( name, who_from, priority, 0, 0 );
4585 void sexp_send_message_list( int n )
4587 char *name, *who_from, *priority;
4594 // send a bunch of messages
4597 who_from = CTEXT(n);
4602 Warning(LOCATION, "Detected incomplete parameter list in sexp-send-message-list");
4605 priority = CTEXT(n);
4610 Warning(LOCATION, "Detected incomplete parameter list in sexp-send-message-list");
4618 Warning(LOCATION, "Detected incomplete parameter list in sexp-send-message-list");
4621 delay += atoi(CTEXT(n));
4624 sexp_send_one_message(name, who_from, priority, 1, delay);
4631 void sexp_send_random_message( int n )
4633 char *name, *who_from, *priority;
4634 int temp, num_messages, message_num;
4636 SDL_assert ( n != -1 );
4637 who_from = CTEXT(n);
4638 priority = CTEXT(CDR(n));
4644 // count the number of messages that we have
4653 if (num_messages == 0) {
4658 // get a random message, and pass the parameters to send_one_message
4659 message_num = myrand() % num_messages;
4662 if ( message_num == 0 )
4667 SDL_assert (n != -1); // should have found the message!!!
4670 sexp_send_one_message( name, who_from, priority, 0, 0 );
4673 void sexp_self_destruct( int n )
4679 // get the ship name and be sure that it is still in the mission. Destroy it if we find it
4680 ship_name = CTEXT(n);
4681 shipnum = ship_name_lookup( ship_name );
4682 if ( shipnum == -1 )
4684 ship_self_destruct( &Objects[Ships[shipnum].objnum] );
4690 void sexp_next_mission( int n )
4695 mission_name = CTEXT(n);
4696 for (i = 0; i < Campaign.num_missions; i++) {
4697 if ( !SDL_strcasecmp(Campaign.missions[i].name, mission_name) ) {
4698 Campaign.next_mission = i;
4702 Error(LOCATION, "Mission name %s not found in campaign file for next-mission command", mission_name);
4705 // function to deal with the end-of-campaign sexpression.
4706 void sexp_end_of_campaign( int n )
4708 // this is really a do-nothing sexpression. It is pretty much a placeholder to allow
4709 // campaigns to have repeat-mission branches at the end of the campaign. By not setting
4710 // anything in this function, the higher level campaign code will see this as end-of-campaign
4711 // since next_mission isn't set to anything. (To be safe, we'll set to -1).
4712 Campaign.next_mission = -1;
4715 // sexpression to end everything. One parameter is the movie to play when this is over.
4716 void sexp_end_campaign( int n )
4718 // post and event to move us to the end-of-campaign state. There we will play a movie, then
4719 // go to debriefing.
4721 // needed to actually end the game
4722 gameseq_post_event( GS_EVENT_END_CAMPAIGN );
4724 // in FS2 our ending is a bit wacky. we'll just flag the mission as having ended the campaign
4725 Campaign_ended_in_mission = 1;
4729 // sabotage subsystem reduces the strength of a subsystem by the given percentage. If it is reduced to
4730 // below 0%, then the hits of the subsystem are set to 0
4731 void sexp_sabotage_subsystem( int n )
4733 char *shipname, *subsystem;
4734 int percentage, shipnum, index;
4735 float sabotage_hits;
4739 shipname = CTEXT(n);
4740 subsystem = CTEXT(CDR(n));
4741 percentage = atoi(CTEXT(CDR(CDR(n))));
4743 shipnum = ship_name_lookup(shipname);
4745 // if no ship, then return immediately.
4746 if ( shipnum == -1 )
4748 shipp = &Ships[shipnum];
4750 // see if we are dealing with the HULL
4751 if ( !SDL_strcasecmp( subsystem, SEXP_HULL_STRING) ) {
4755 ihs = Ship_info[shipp->ship_info_index].initial_hull_strength;
4756 sabotage_hits = ihs * ((float)percentage / 100.0f);
4757 objp = &Objects[shipp->objnum];
4758 objp->hull_strength -= sabotage_hits;
4760 // self destruct the ship if <= 0.
4761 if ( objp->hull_strength <= 0.0f )
4762 ship_self_destruct( objp );
4766 // now find the given subsystem on the ship. The subsystem should be an actual subsystem name
4767 // and not a generic type (generic type meaning SUBSYSTEM_ENGINE, etc).
4769 index = ship_get_subsys_index(shipp, subsystem);
4770 if ( index == -1 ) {
4771 nprintf(("Warning", "Couldn't find subsystem %s on ship %s for sabotage subsystem\n", subsystem, shipp->ship_name));
4775 // get the pointer to the subsystem. Check it's current hits against it's max hits, and
4776 // set the strength to the given percentage if current strength is > given percentage
4777 ss = ship_get_indexed_subsys( shipp, index );
4778 sabotage_hits = ss->system_info->max_hits * ((float)percentage / 100.0f);
4779 ss->current_hits -= sabotage_hits;
4780 if ( ss->current_hits < 0.0f )
4781 ss->current_hits = 0.0f;
4782 ship_recalc_subsys_strength( shipp );
4785 // repair_subsystem adds some percentage of hits to a subsystem. Anything repaired about 100% is
4787 void sexp_repair_subsystem( int n )
4789 char *shipname, *subsystem;
4790 int percentage, shipnum, index;
4795 shipname = CTEXT(n);
4796 subsystem = CTEXT(CDR(n));
4797 shipnum = ship_name_lookup(shipname);
4799 // if no ship, then return immediately.
4800 if ( shipnum == -1 ) {
4803 shipp = &Ships[shipnum];
4805 // check if we've got a number or an op
4806 if ( CAR(CDR(CDR(n))) != -1) {
4807 percentage = eval_sexp( CAR(CDR(CDR(n))) );
4809 percentage = atoi(CTEXT(CDR(CDR(n))));
4812 // see if we are dealing with the HULL
4813 if ( !SDL_strcasecmp( subsystem, SEXP_HULL_STRING) ) {
4817 ihs = Ship_info[shipp->ship_info_index].initial_hull_strength;
4818 repair_hits = ihs * ((float)percentage / 100.0f);
4819 objp = &Objects[shipp->objnum];
4820 objp->hull_strength += repair_hits;
4821 if ( objp->hull_strength > ihs )
4822 objp->hull_strength = ihs;
4826 // now find the given subsystem on the ship. The subsystem should be an actual subsystem name
4827 // and not a generic type (generic type meaning SUBSYSTEM_ENGINE, etc).
4829 index = ship_get_subsys_index(shipp, subsystem);
4830 if ( index == -1 ) {
4831 nprintf(("Warning", "Couldn't find subsystem %s on ship %s for repair subsystem\n", subsystem, shipp->ship_name));
4835 // get the pointer to the subsystem. Check it's current hits against it's max hits, and
4836 // set the strength to the given percentage if current strength is < given percentage
4837 ss = ship_get_indexed_subsys( shipp, index );
4838 repair_hits = ss->system_info->max_hits * ((float)percentage / 100.0f);
4839 ss->current_hits += repair_hits;
4840 if ( ss->current_hits > ss->system_info->max_hits )
4841 ss->current_hits = ss->system_info->max_hits;
4842 ship_recalc_subsys_strength( shipp );
4845 // sexpression code to set a subsystem of a ship at a specific percentage
4846 void sexp_set_subsystem_strength( int n )
4848 char *shipname, *subsystem;
4849 int percentage, shipnum, index;
4853 shipname = CTEXT(n);
4854 subsystem = CTEXT(CDR(n));
4855 percentage = num_eval(CDR(CDR(n)));
4857 shipnum = ship_name_lookup(shipname);
4859 // if no ship, then return immediately.
4860 if ( shipnum == -1 )
4862 shipp = &Ships[shipnum];
4864 if ( percentage > 100 ) {
4865 nprintf(("Warning", "percentage for set_subsystem_strength > 100 -- setting to 100\n"));
4867 } else if ( percentage < 0 ) {
4868 nprintf(("Werning", "percantage for set_subsystem_strength < 0 -- setting to 0\n"));
4872 // see if we are dealing with the HULL
4873 if ( !SDL_strcasecmp( subsystem, SEXP_HULL_STRING) ) {
4877 objp = &Objects[shipp->objnum];
4879 // destroy the ship if percentage is 0
4880 if ( percentage == 0 ) {
4881 ship_self_destruct( objp );
4883 ihs = Ship_info[shipp->ship_info_index].initial_hull_strength;
4884 objp->hull_strength = ihs * ((float)percentage / 100.0f);
4890 // now find the given subsystem on the ship. The subsystem should be an actual subsystem name
4891 // and not a generic type (generic type meaning SUBSYSTEM_ENGINE, etc).
4893 index = ship_get_subsys_index(shipp, subsystem);
4894 if ( index == -1 ) {
4895 nprintf(("Warning", "Couldn't find subsystem %s on ship %s for repair subsystem\n", subsystem, shipp->ship_name));
4899 // get the pointer to the subsystem. Check it's current hits against it's max hits, and
4900 // set the strength to the given percentage if current strength is < given percentage
4901 ss = ship_get_indexed_subsys( shipp, index );
4903 // maybe blow up subsys
4904 if (ss->current_hits > 0) {
4905 if (percentage < 1) {
4906 do_subobj_destroyed_stuff(shipp, ss, NULL);
4911 ss->current_hits = ss->system_info->max_hits * ((float)percentage / 100.0f);
4913 ship_recalc_subsys_strength( shipp );
4916 // function which changes the validity of a goal. The flag paramater tells us whether to mark the goals
4917 // as valid or invalid
4918 void sexp_change_goal_validity( int n, int flag )
4925 mission_goal_mark_valid( name );
4927 mission_goal_mark_invalid( name );
4933 // function to transfer cargo from one ship to another
4934 void sexp_transfer_cargo( int n )
4936 char *shipname1, *shipname2;
4937 int shipnum1, shipnum2, i;
4940 shipname1 = CTEXT(n);
4941 shipname2 = CTEXT(CDR(n));
4943 // find the ships -- if neither in the mission, the abort
4944 shipnum1 = ship_name_lookup(shipname1);
4945 shipnum2 = ship_name_lookup(shipname2);
4946 if ( (shipnum1 == -1) || (shipnum2 == -1) )
4949 // we must be sure that these two objects are indeed docked
4950 objp = ai_find_docked_object( &Objects[Ships[shipnum1].objnum] );
4951 if ( objp != &Objects[Ships[shipnum2].objnum] ) {
4952 Int3(); // you are trying to transfer cargo between two ships not docked
4956 if ( !SDL_strcasecmp(Cargo_names[Ships[shipnum1].cargo1 & CARGO_INDEX_MASK], "nothing") ) {
4957 Int3(); // you are transfering no cargo!!!!
4961 // transfer cargo from ship1 to ship2
4963 // Don't give warning for large ships (cruiser on up)
4964 if (! (Ship_info[Ships[shipnum2].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
4965 if ( SDL_strcasecmp(Cargo_names[Ships[shipnum2].cargo1 & CARGO_INDEX_MASK], "nothing") ) {
4966 Warning(LOCATION, "Transfering cargo to %s which already\nhas cargo %s.\nCargo will be replaced", Ships[shipnum2].ship_name, Cargo_names[Ships[shipnum2].cargo1 & CARGO_INDEX_MASK] );
4970 Ships[shipnum2].cargo1 = char(Ships[shipnum1].cargo1 & CARGO_INDEX_MASK);
4972 if ( !(Ships[shipnum1].cargo1 & CARGO_NO_DEPLETE) ) {
4973 // need to set ship1's cargo to nothing. scan the cargo_names array looking for the string nothing.
4974 // add it if not found
4975 for (i = 0; i < Num_cargo; i++ ) {
4976 if ( !SDL_strcasecmp(Cargo_names[i], "nothing") ) {
4977 Ships[shipnum1].cargo1 = char(i);
4981 SDL_strlcpy(Cargo_names[i], "Nothing", NAME_LENGTH);
4986 // this function exchanges cargo between two ships
4987 void sexp_exchange_cargo( int n )
4989 char *shipname1, *shipname2;
4990 int shipnum1, shipnum2, temp;
4993 shipname1 = CTEXT(n);
4994 shipname2 = CTEXT(CDR(n));
4996 // find the ships -- if neither in the mission, the abort
4997 shipnum1 = ship_name_lookup(shipname1);
4998 shipnum2 = ship_name_lookup(shipname2);
4999 if ( (shipnum1 == -1) || (shipnum2 == -1) )
5002 // we must be sure that these two objects are indeed docked
5003 objp = ai_find_docked_object( &Objects[Ships[shipnum1].objnum] );
5004 if ( objp != &Objects[Ships[shipnum2].objnum] ) {
5005 Int3(); // you are trying to transfer cargo between two ships not docked
5009 temp = (Ships[shipnum1].cargo1 & CARGO_INDEX_MASK);
5010 Ships[shipnum1].cargo1 = char(Ships[shipnum2].cargo1 & CARGO_INDEX_MASK);
5011 Ships[shipnum2].cargo1 = char(temp);
5014 void sexp_cap_waypont_speed(int n)
5020 shipname = CTEXT(n);
5021 speed = atoi(CTEXT(CDR(n)));
5023 shipnum = ship_name_lookup(shipname);
5025 if (shipnum == -1) {
5026 Int3(); // trying to set waypoint speed of ship not already in game
5030 // cap speed to range (-1, 127) to store within char
5039 Ai_info[Ships[shipnum].ai_index].waypoint_speed_cap = (char) speed;
5042 // this function causes a ship to jettison its cargo
5043 void sexp_jettison_cargo( int n )
5046 int ship_index;//, jettison_delay;
5049 shipname = CTEXT(n);
5050 // jettison_delay = atoi(CTEXT(CDR(n)));
5053 ship_index = ship_name_lookup(shipname);
5059 ship_jettison_cargo(&Ships[ship_index]);
5062 void sexp_cargo_no_deplete( int n )
5065 int ship_index, no_deplete = 1;
5068 shipname = CTEXT(n);
5071 ship_index = ship_name_lookup(shipname);
5076 if ( !(Ship_info[Ships[ship_index].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
5077 Warning(LOCATION, "Trying to make non BIG or HUGE ship %s with non-depletable cargo.\n", Ships[ship_index].ship_name);
5082 no_deplete = atoi(CTEXT(CDR(n)));
5083 SDL_assert((no_deplete == 0) || (no_deplete == 1));
5084 if ( (no_deplete != 0) && (no_deplete != 1) ) {
5090 Ships[ship_index].cargo1 |= CARGO_NO_DEPLETE;
5092 Ships[ship_index].cargo1 &= (~CARGO_NO_DEPLETE);
5097 // sexpression to end the mission after N seconds!
5098 void sexp_end_mission_delay( int n )
5102 //delay = atoi(CTEXT(n));
5103 //mission_parse_set_end_time( delay );
5104 mprintf(("Not ending mission -- end-mission sexpression no longer works!\n"));
5107 // funciton to toggle the status bit for the AI code which tells the AI if it is a good time
5108 // to rearm. The status being set means good time. Status not being set (unset), means bad time.
5109 // designers must implement this.
5110 void sexp_good_time_to_rearm( int n )
5115 team_name = CTEXT(n);
5116 time = atoi(CTEXT(CDR(n))); // this is the time for how long a good rearm is active -- in seconds
5117 for ( i = 0; i < Num_team_names; i++ ) {
5118 if ( !SDL_strcasecmp(team_name, Team_names[i]) ) {
5122 ai_set_rearm_status( team, time );
5127 // function which grants promotion to the player
5128 void sexp_grant_promotion()
5130 // short circuit multiplayer for now until we figure out what to do.
5131 if ( Game_mode & GM_MULTIPLAYER )
5134 // set a bit to tell player should get promoted at the end of the mission. I suppose the other
5135 // thing that we could do would be to set the players score to at least the amount of
5136 // points for the next level, but this way is better I think.
5137 if ( Game_mode & GM_CAMPAIGN_MODE ) {
5138 Player->flags |= PLAYER_FLAGS_PROMOTED;
5142 // function which gives the named medal to the players in the mission
5143 void sexp_grant_medal( int n )
5148 // don't give medals in normal gameplay when not in campaign mode
5149 if ( (Game_mode & GM_NORMAL) && !(Game_mode & GM_CAMPAIGN_MODE) )
5152 SDL_assert(Player->stats.m_medal_earned < 0); // Mission has problems. Tried to grant 2 medals in 1 mission.
5153 medal_name = CTEXT(n);
5154 for (i = 0; i < NUM_MEDALS; i++ ) {
5155 if ( !SDL_strcasecmp(medal_name, Medals[i].name) )
5159 if ( i < NUM_MEDALS ) {
5160 Player->stats.m_medal_earned = i;
5161 if ( Game_mode & GM_MULTIPLAYER ) {
5162 for ( j = 0; j < MAX_PLAYERS; j++ ) {
5163 if ( MULTI_CONNECTED(Net_players[j]) ) {
5164 Net_players[j].player->stats.m_medal_earned = i;
5171 void sexp_tech_add_ship(int node)
5176 SDL_assert(node >= 0);
5177 // this function doesn't mean anything when not in campaign mode
5178 if ( !(Game_mode & GM_CAMPAIGN_MODE) )
5183 i = ship_info_lookup(name);
5185 Ship_info[i].flags |= SIF_IN_TECH_DATABASE;
5187 Error(LOCATION, "Ship class \"%s\" invalid", name);
5193 void sexp_tech_add_weapon(int node)
5198 SDL_assert(node >= 0);
5199 // this function doesn't mean anything when not in campaign mode
5200 if ( !(Game_mode & GM_CAMPAIGN_MODE) )
5205 i = weapon_info_lookup(name);
5207 Weapon_info[i].wi_flags |= WIF_IN_TECH_DATABASE;
5209 Error(LOCATION, "Ship class \"%s\" invalid", name);
5215 // function to set variables needed to grant a new ship/weapon to the player during the course
5217 void sexp_allow_ship( int n )
5222 // this function doesn't mean anything when not in campaign mode
5223 if ( !(Game_mode & GM_CAMPAIGN_MODE) )
5226 // get the name of the ship and lookup up the ship_info index for it
5228 sindex = ship_info_lookup( name );
5232 // now we have a valid index --
5233 mission_campaign_save_persistent( CAMPAIGN_PERSISTENT_SHIP, sindex );
5236 void sexp_allow_weapon( int n )
5241 // this function doesn't mean anything when not in campaign mode
5242 if ( !(Game_mode & GM_CAMPAIGN_MODE) )
5245 // get the name of the weapon and lookup up the weapon_info index for it
5247 sindex = weapon_info_lookup( name );
5251 // now we have a valid index --
5252 mission_campaign_save_persistent( CAMPAIGN_PERSISTENT_WEAPON, sindex );
5255 // functions to deal with breaking/fixing the warp engines on ships/wings. should_break is true when
5256 // we are breaking the warp drive (can be repaired). The parameter is 0 when is getting broken (i.e.
5257 // can be fixed by repair). The repair parameter tells us whether we are clearing the destroyed or broken
5258 // flag (1), or setting them (0).
5259 void sexp_deal_with_warp( int n, int should_break, int nix )
5264 for ( ; n != -1; n = CDR(n) ) {
5266 index = ship_name_lookup(name);
5268 // check to see if ship destroyed or departed. In either case, do nothing.
5269 if ( mission_log_get_time(LOG_SHIP_DEPART, name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) )
5272 // we can only operate on ships which are in the mission
5273 if ( index != -1 ) {
5275 // set the flag value accoring to whether we are destroying the warp or just breaking it
5277 flag = SF_WARP_BROKEN;
5279 flag = SF_WARP_NEVER;
5282 Ships[index].flags |= flag;
5284 Ships[index].flags &= ~flag;
5287 // maybe this ship has yet to arrive. Get a possible parse object and set the flag if found
5290 pobjp = mission_parse_get_arrival_ship( name );
5292 if ( pobjp == NULL ) {
5293 Int3(); // warning, find allender -- should be impossible
5298 flag = P_SF_WARP_BROKEN;
5300 flag = P_SF_WARP_BROKEN;
5303 pobjp->flags |= flag;
5305 pobjp->flags &= ~flag;
5311 // function which is used to tell the AI when it is okay to fire certain secondary
5312 // weapons at other ships.
5313 void sexp_good_secondary_time( int n )
5315 char *team_name, *weapon_name, *ship_name;
5316 int num_weapons, weapon_index, team, i;
5318 team_name = CTEXT(n);
5319 num_weapons = atoi(CTEXT(CDR(n)));
5320 weapon_name = CTEXT(CDR(CDR(n)));
5321 ship_name = CTEXT(CDR(CDR(CDR(n))));
5323 weapon_index = weapon_info_lookup(weapon_name);
5324 if ( weapon_index == -1 ) {
5325 nprintf(("Warning", "couldn't find weapon %s for good-secondary-time\n", weapon_name));
5329 // get the team type from the team_name
5330 for ( i = 0; i < Num_team_names; i++ ) {
5331 if ( !SDL_strcasecmp(Team_names[i], team_name) )
5334 if ( i == Num_team_names ) {
5335 nprintf(("Warning", "couldn't find team %s for good-secondary-time\n", team_name ));
5338 team = (1<<i); // this is the magic formula to get to a team type.
5340 // see if the ship has departed or has been destroyed. If so, then we don't need to set up the
5342 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5345 ai_good_secondary_time( team, weapon_index, num_weapons, ship_name );
5349 // function to deal with getting status of goals for previous missions (in the current campaign).
5350 // the status parameter is used to tell this function if we are looking for a goal_satisfied, goal_failed,
5351 // or goal incomplete event
5352 int sexp_previous_goal_status( int n, int status )
5354 char rval = 0, *mission_name;
5356 int i, mission_num, default_value = 0, use_defaults = 1;
5358 mission_name = CTEXT(n);
5359 goal_name = CTEXT(CDR(n));
5361 // check for possible next optional argument
5364 default_value = eval_sexp(n);
5367 // try to find the given mission name in the current list of missions in the campaign.
5368 if ( Game_mode & GM_CAMPAIGN_MODE ) {
5369 i = mission_campaign_find_mission( mission_name );
5372 // if mission not found, assume that goal was false (so previous-goal-false returns true)
5373 nprintf(("General", "Couldn't find mission name %s in current campaign's list of missions.\nReturning %s for goal-status function.", mission_name, (status==GOAL_COMPLETE)?"false":"true"));
5374 if ( status == GOAL_COMPLETE )
5375 rval = SEXP_KNOWN_FALSE;
5377 rval = SEXP_KNOWN_TRUE;
5380 } else if (Campaign.missions[i].flags & CMISSION_FLAG_SKIPPED) {
5383 // now try and find the goal this mission
5385 for (i = 0; i < Campaign.missions[mission_num].num_goals; i++) {
5386 if ( !SDL_strcasecmp(Campaign.missions[mission_num].goals[i].name, goal_name) )
5390 if ( i == Campaign.missions[mission_num].num_goals ) {
5391 Warning(LOCATION, "Couldn't find goal name %s in mission %s.\nReturning %s for goal-true function.", goal_name, mission_name, (status==GOAL_COMPLETE)?"false":"true");
5392 if ( status == GOAL_COMPLETE )
5393 rval = SEXP_KNOWN_FALSE;
5395 rval = SEXP_KNOWN_TRUE;
5398 // now return KNOWN_TRUE or KNOWN_FALSE based on the status field in the goal structure
5399 if ( Campaign.missions[mission_num].goals[i].status == status )
5400 rval = SEXP_KNOWN_TRUE;
5402 rval = SEXP_KNOWN_FALSE;
5410 // when not in campaign mode, always return KNOWN_TRUE when looking for goal complete, and KNOWN_FALSE
5413 if ( default_value )
5414 rval = SEXP_KNOWN_TRUE;
5416 rval = SEXP_KNOWN_FALSE;
5418 if ( status == GOAL_COMPLETE )
5419 rval = SEXP_KNOWN_TRUE;
5421 rval = SEXP_KNOWN_FALSE;
5428 // sexpression which gets the status of an event from a previous mission. Like the above function but
5429 // dealing with events instead of goals. Again, the status parameter tells the code if we are looking
5430 // for an event_true, event_false, or event_incomplete status
5431 int sexp_previous_event_status( int n, int status )
5433 char rval = 0, *mission_name;
5435 int i, mission_num, default_value = 0, use_defaults = 1;
5437 mission_name = CTEXT(n);
5438 name = CTEXT(CDR(n));
5440 // check for possible optional parameter
5443 default_value = eval_sexp(n);
5446 if ( Game_mode & GM_CAMPAIGN_MODE ) {
5447 // following function returns -1 when mission isn't found.
5448 i = mission_campaign_find_mission( mission_name );
5450 // if the mission name wasn't found -- make this return FALSE for the event status.
5452 nprintf(("General", "Couldn't find mission name %s in current campaign's list of missions.\nReturning %s for event-status function.", mission_name, (status==EVENT_SATISFIED)?"false":"true"));
5453 if ( status == EVENT_SATISFIED ) {
5454 rval = SEXP_KNOWN_FALSE;
5456 rval = SEXP_KNOWN_TRUE;
5460 } else if (Campaign.missions[i].flags & CMISSION_FLAG_SKIPPED) {
5463 // now try and find the goal this mission
5465 for (i = 0; i < Campaign.missions[mission_num].num_events; i++) {
5466 if ( !SDL_strcasecmp(Campaign.missions[mission_num].events[i].name, name) )
5470 if ( i == Campaign.missions[mission_num].num_events ) {
5471 Warning(LOCATION, "Couldn't find event name %s in mission %s.\nReturning %s for event_status function.", name, mission_name, (status==EVENT_SATISFIED)?"false":"true");
5472 if ( status == EVENT_SATISFIED )
5473 rval = SEXP_KNOWN_FALSE;
5475 rval = SEXP_KNOWN_TRUE;
5478 // now return KNOWN_TRUE or KNOWN_FALSE based on the status field in the goal structure
5479 if ( Campaign.missions[mission_num].events[i].status == status )
5480 rval = SEXP_KNOWN_TRUE;
5482 rval = SEXP_KNOWN_FALSE;
5491 if ( default_value )
5492 rval = SEXP_KNOWN_TRUE;
5494 rval = SEXP_KNOWN_FALSE;
5496 if ( status == EVENT_SATISFIED )
5497 rval = SEXP_KNOWN_TRUE;
5499 rval = SEXP_KNOWN_FALSE;
5506 // function to return the status of an event in the current mission. The passed parameter indicates
5507 // if we are checking whether the event is true or the event is false.
5508 int sexp_event_status( int n, int want_true )
5514 for (i = 0; i < Num_mission_events; i++ ) {
5515 // look for the event name, check it's status. If formula is gone, we know the state won't ever change.
5516 if ( !SDL_strcasecmp(Mission_events[i].name, name) ) {
5517 result = Mission_events[i].result;
5518 if (Mission_events[i].formula < 0) {
5519 if ( (want_true && result) || (!want_true && !result) )
5520 return SEXP_KNOWN_TRUE;
5522 return SEXP_KNOWN_FALSE;
5525 if ( (want_true && result) || (!want_true && !result) )
5536 // function to return the status of an event N seconds after the event is true or false. Similar
5537 // to above function but waits N seconds before returning true
5538 int sexp_event_delay_status( int n, int want_true )
5545 delay = i2f(num_eval(CDR(n)));
5546 for (i = 0; i < Num_mission_events; i++ ) {
5547 // look for the event name, check it's status. If formula is gone, we know the state won't ever change.
5548 if ( !SDL_strcasecmp(Mission_events[i].name, name) ) {
5549 if ( (fix) Mission_events[i].timestamp + delay >= Missiontime )
5552 result = Mission_events[i].result;
5553 if (Mission_events[i].formula < 0) {
5554 if ( (want_true && result) || (!want_true && !result) )
5555 return SEXP_KNOWN_TRUE;
5557 return SEXP_KNOWN_FALSE;
5560 if ( want_true && result ) //) || (!want_true && !result) )
5571 // function which returns true if the given event is still incomplete
5572 int sexp_event_incomplete( int n )
5579 for (i = 0; i < Num_mission_events; i++ ) {
5580 if ( !SDL_strcasecmp(Mission_events[i].name, name ) ) {
5581 // if the formula is still >= 0 (meaning it is still getting eval'ed), then
5582 // the event is incomplete
5583 if ( Mission_events[i].formula != -1 )
5586 return SEXP_KNOWN_FALSE;
5593 // function to return the status of an goal N seconds after the goal is true or false. Similar
5594 // to above function but operates on goals instead of events
5595 int sexp_goal_delay_status( int n, int want_true )
5601 delay = i2f(num_eval(CDR(n)));
5604 // if we are looking for a goal true entry and we find a false, then return known false here
5605 if ( mission_log_get_time(LOG_GOAL_FAILED, name, NULL, NULL) )
5606 return SEXP_KNOWN_FALSE;
5607 else if ( mission_log_get_time(LOG_GOAL_SATISFIED, name, NULL, &time) ) {
5608 if ( (Missiontime - time) >= delay )
5609 return SEXP_KNOWN_TRUE;
5612 // if we are looking for a goal false entry and we find a true, then return known false here
5613 if ( mission_log_get_time(LOG_GOAL_SATISFIED, name, NULL, NULL) )
5614 return SEXP_KNOWN_FALSE;
5615 else if ( mission_log_get_time(LOG_GOAL_FAILED, name, NULL, &time) ) {
5616 if ( (Missiontime - time) >= delay )
5617 return SEXP_KNOWN_TRUE;
5624 // function which returns true if the given goal is still incomplete
5625 int sexp_goal_incomplete( int n )
5631 if ( mission_log_get_time( LOG_GOAL_SATISFIED, name, NULL, NULL) || mission_log_get_time( LOG_GOAL_FAILED, name, NULL, NULL) )
5632 return SEXP_KNOWN_FALSE;
5638 // protects/unprotects a ship. The flag tells us whether or not the protect bit should be set (flag==1)
5639 // or cleared (flag==0)
5640 void sexp_protect_ships( int n, int flag )
5646 ship_name = CTEXT(n);
5648 // check to see if ship destroyed or departed. In either case, do nothing.
5649 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5652 // get the ship num. If we get a -1 for the number here, ship has yet to arrive. Store this ship
5653 // in a list until created
5654 num = ship_name_lookup(ship_name);
5657 Objects[Ships[num].objnum].flags |= OF_PROTECTED;
5659 Objects[Ships[num].objnum].flags &= ~OF_PROTECTED;
5661 p_object *parse_obj;
5663 parse_obj = mission_parse_get_arrival_ship( ship_name );
5666 parse_obj->flags |= P_OF_PROTECTED;
5668 parse_obj->flags &= ~P_OF_PROTECTED;
5672 Int3(); // get allender -- could be a potential problem here
5681 // protects/unprotects a ship. The flag tells us whether or not the protect bit should be set (flag==1)
5682 // or cleared (flag==0)
5683 void sexp_beam_protect_ships( int n, int flag )
5689 ship_name = CTEXT(n);
5691 // check to see if ship destroyed or departed. In either case, do nothing.
5692 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5695 // get the ship num. If we get a -1 for the number here, ship has yet to arrive. Store this ship
5696 // in a list until created
5697 num = ship_name_lookup(ship_name);
5700 Objects[Ships[num].objnum].flags |= OF_BEAM_PROTECTED;
5702 Objects[Ships[num].objnum].flags &= ~OF_BEAM_PROTECTED;
5704 p_object *parse_obj;
5706 parse_obj = mission_parse_get_arrival_ship( ship_name );
5709 parse_obj->flags |= P_OF_BEAM_PROTECTED;
5711 parse_obj->flags &= ~P_OF_BEAM_PROTECTED;
5715 Int3(); // get allender -- could be a potential problem here
5724 // sexpression to make ships "visible" and "invisible" to sensors. The visible parameter is true
5725 // when making ships visible, false otherwise
5726 void sexp_ships_visible( int n, int visible )
5731 for ( ; n != -1; n = CDR(n) ) {
5732 ship_name = CTEXT(n);
5734 // check to see if ship destroyed or departed. In either case, do nothing.
5735 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5738 // get the ship num. If we get a -1 for the number here, ship has yet to arrive. Store this ship
5739 // in a list until created
5740 num = ship_name_lookup(ship_name);
5743 Ships[num].flags |= SF_HIDDEN_FROM_SENSORS;
5745 Ships[num].flags &= ~SF_HIDDEN_FROM_SENSORS;
5746 if (Ships[num].flags & SF_ESCORT) {
5747 // SEND add escort request
5748 hud_add_ship_to_escort(Ships[num].objnum, 1);
5753 p_object *parse_obj;
5755 parse_obj = mission_parse_get_arrival_ship( ship_name );
5758 parse_obj->flags |= P_SF_HIDDEN_FROM_SENSORS;
5760 parse_obj->flags &= ~P_SF_HIDDEN_FROM_SENSORS;
5764 Int3(); // get allender -- could be a potential problem here
5771 // sexpression to toggle invulnerability flag of ships.
5772 void sexp_ships_invulnerable( int n, int invulnerable )
5778 for ( ; n != -1; n = CDR(n) ) {
5779 ship_name = CTEXT(n);
5781 // check to see if ship destroyed or departed. In either case, do nothing.
5782 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5785 // get the ship num. If we get a -1 for the number here, ship has yet to arrive. Store this ship
5786 // in a list until created
5787 num = ship_name_lookup(ship_name);
5789 objp = &Objects[Ships[num].objnum];
5791 objp->flags |= OF_INVULNERABLE;
5793 objp->flags &= ~OF_INVULNERABLE;
5795 p_object *parse_obj;
5797 parse_obj = mission_parse_get_arrival_ship( ship_name );
5800 parse_obj->flags |= P_SF_INVULNERABLE;
5802 parse_obj->flags &= ~P_SF_INVULNERABLE;
5806 Int3(); // get allender -- could be a potential problem here
5813 // sexpression to toggle KEEP ALIVE flag of ship object
5814 void sexp_ships_guardian( int n, int guardian )
5820 for ( ; n != -1; n = CDR(n) ) {
5821 ship_name = CTEXT(n);
5823 // check to see if ship destroyed or departed. In either case, do nothing.
5824 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5827 // get the ship num. If we get a -1 for the number here, ship has yet to arrive. Store this ship
5828 // in a list until created
5829 num = ship_name_lookup(ship_name);
5831 objp = &Objects[Ships[num].objnum];
5833 objp->flags |= OF_GUARDIAN;
5835 objp->flags &= ~OF_GUARDIAN;
5837 p_object *parse_obj;
5839 parse_obj = mission_parse_get_arrival_ship( ship_name );
5842 parse_obj->flags |= P_SF_GUARDIAN;
5844 parse_obj->flags &= ~P_SF_GUARDIAN;
5848 Int3(); // get allender -- could be a potential problem here
5855 // make ship vanish without a trace (and what its docked to)
5856 void ship_vanished(int);
5857 void sexp_ship_vanish( int n )
5860 object *objp, *docked_objp;
5863 // if MULTIPLAYER bail
5864 if (Game_mode & GM_MULTIPLAYER) {
5868 for ( ; n != -1; n = CDR(n) ) {
5869 ship_name = CTEXT(n);
5871 // check to see if ship destroyed or departed. In either case, do nothing.
5872 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5875 // get the ship num. If we get a -1 for the number here, ship has yet to arrive. Store this ship
5876 // in a list until created
5877 num = ship_name_lookup(ship_name);
5879 objp = &Objects[Ships[num].objnum];
5882 docked_objp = ai_find_docked_object( objp );
5885 objp->flags |= OF_SHOULD_BE_DEAD;
5892 docked_objp->flags |= OF_SHOULD_BE_DEAD;
5895 ship_vanished(docked_objp->instance);
5901 int sexp_key_pressed(int node)
5905 SDL_assert(node != -1);
5906 z = translate_key_to_index(CTEXT(node));
5911 if (!Control_config[z].used){
5919 t = atoi(CTEXT(CDR(node)));
5920 return timestamp_has_time_elapsed(Control_config[z].used, t * 1000);
5923 void sexp_key_reset(int node)
5927 SDL_assert(node != -1);
5928 z = translate_key_to_index(CTEXT(node));
5930 Control_config[z].used = 0;
5933 int sexp_targeted(int node)
5938 z = ship_query_state(CTEXT(node));
5940 return SEXP_KNOWN_FALSE; // ship isn't around, nor will it ever be
5941 } else if (z == -1) {
5942 return SEXP_CANT_EVAL;
5945 z = ship_name_lookup(CTEXT(node), 1);
5946 if ((z < 0) || !Player_ai || (Ships[z].objnum != Player_ai->target_objnum)){
5950 if (CDR(node) >= 0) {
5951 z = atoi(CTEXT(CDR(node))) * 1000;
5952 if (!timestamp_has_time_elapsed(Players_target_timestamp, z)){
5956 if (CDR(CDR(node)) >= 0) {
5957 ptr = Player_ai->targeted_subsys;
5958 if (!ptr || SDL_strcasecmp(ptr->system_info->subobj_name, CTEXT(CDR(CDR(node))))){
5967 int sexp_speed(int node)
5969 if (Training_context & TRAINING_CONTEXT_SPEED) {
5970 if (Training_context_speed_set){
5971 if (timestamp_has_time_elapsed(Training_context_speed_timestamp, atoi(CTEXT(node)) * 1000)){
5972 return SEXP_KNOWN_TRUE;
5980 int sexp_secondaries_depleted(int node)
5982 int sindex, num_banks, num_depleted_banks;
5986 sindex = ship_name_lookup(CTEXT(node));
5991 shipp = &Ships[sindex];
5992 if (shipp->objnum < 0) {
5996 // get num secondary banks
5997 num_banks = shipp->weapons.num_secondary_banks;
5998 num_depleted_banks = 0;
6000 // get number of depleted banks
6001 for (int idx=0; idx<num_banks; idx++) {
6002 if (shipp->weapons.secondary_bank_ammo[idx] == 0) {
6003 num_depleted_banks++;
6007 // are they all depleted?
6008 return (num_depleted_banks == num_banks);
6011 int sexp_facing(int node)
6017 if (ship_query_state(CTEXT(node)) < 0){
6018 return SEXP_KNOWN_FALSE;
6021 sh = ship_name_lookup(CTEXT(node));
6022 if ((sh < 0) || !Player_obj){
6026 obj = Ships[sh].objnum;
6027 v1 = Player_obj->orient.v.fvec;
6028 vm_vec_normalize(&v1);
6029 vm_vec_sub(&v2, &Objects[obj].pos, &Player_obj->pos);
6030 vm_vec_normalize(&v2);
6031 a1 = vm_vec_dotprod(&v1, &v2);
6032 a2 = (float) cos(ANG_TO_RAD(atof(CTEXT(CDR(node)))));
6040 // is ship facing first waypoint in waypoint path
6041 int sexp_facing2(int node)
6047 // bail if Player_obj is not good
6049 return SEXP_CANT_EVAL;
6053 v1 = Player_obj->orient.v.fvec;
6054 vm_vec_normalize(&v1);
6056 // get waypoint name
6057 char *waypoint_name = CTEXT(node);
6059 // get position of first waypoint
6061 for (i=0; i<Num_waypoint_lists; i++) {
6062 if (!SDL_strcasecmp(waypoint_name, Waypoint_lists[i].name)) {
6068 if (wp_index == -1) {
6069 return SEXP_CANT_EVAL;
6072 // Waypoint_lists[wp_index].waypoints[0]
6074 vm_vec_sub(&v2, &Waypoint_lists[wp_index].waypoints[0], &Player_obj->pos);
6075 vm_vec_normalize(&v2);
6076 a1 = vm_vec_dotprod(&v1, &v2);
6077 a2 = (float) cos(ANG_TO_RAD(atof(CTEXT(CDR(node)))));
6085 int sexp_order(int node)
6090 int sexp_waypoint_missed()
6092 if (Training_context & TRAINING_CONTEXT_FLY_PATH) {
6093 if (Training_context_at_waypoint > Training_context_goal_waypoint){
6101 int sexp_waypoint_twice()
6103 if (Training_context & TRAINING_CONTEXT_FLY_PATH) {
6104 if (Training_context_at_waypoint < Training_context_goal_waypoint - 1){
6112 int sexp_path_flown()
6114 if (Training_context & TRAINING_CONTEXT_FLY_PATH) {
6115 if (Training_context_goal_waypoint == Waypoint_lists[Training_context_path].count){
6123 void sexp_send_training_message(int node)
6125 int t = -1, delay = 0;
6131 SDL_assert(node >= 0);
6132 SDL_assert(Event_index >= 0);
6134 if ((CDR(node) >= 0) && (CDR(CDR(node)) >= 0)) {
6135 delay = atoi(CTEXT(CDR(CDR(node)))) * 1000;
6136 t = CDR(CDR(CDR(node)));
6142 if ((Mission_events[Event_index].repeat_count > 1) || (CDR(node) < 0)){
6143 message_training_que(CTEXT(node), timestamp(delay), t);
6145 message_training_que(CTEXT(CDR(node)), timestamp(delay), t);
6148 // if (Training_msg_method)
6149 // gameseq_post_event(GS_EVENT_TRAINING_PAUSE);
6152 int sexp_shield_recharge_pct(int node)
6156 // get the firing ship
6157 sindex = ship_name_lookup(CTEXT(node));
6161 if(Ships[sindex].objnum < 0){
6165 // shield recharge pct
6166 return (int)(100.0f * Energy_levels[Ships[sindex].shield_recharge_index]);
6169 int sexp_engine_recharge_pct(int node)
6173 // get the firing ship
6174 sindex = ship_name_lookup(CTEXT(node));
6178 if(Ships[sindex].objnum < 0){
6182 // shield recharge pct
6183 return (int)(100.0f * Energy_levels[Ships[sindex].engine_recharge_index]);
6186 int sexp_weapon_recharge_pct(int node)
6190 // get the firing ship
6191 sindex = ship_name_lookup(CTEXT(node));
6195 if(Ships[sindex].objnum < 0){
6199 // shield recharge pct
6200 return (int)(100.0f * Energy_levels[Ships[sindex].weapon_recharge_index]);
6203 int sexp_shield_quad_low(int node)
6206 float max_quad, check;
6211 sindex = ship_name_lookup(CTEXT(node));
6215 if((Ships[sindex].objnum < 0) || (Ships[sindex].objnum >= MAX_OBJECTS)){
6218 if((Ships[sindex].ship_info_index < 0) || (Ships[sindex].ship_info_index >= Num_ship_types)){
6221 objp = &Objects[Ships[sindex].objnum];
6222 sip = &Ship_info[Ships[sindex].ship_info_index];
6223 if(!(sip->flags & SIF_SMALL_SHIP)){
6226 max_quad = sip->shields / (float)MAX_SHIELD_SECTIONS;
6229 check = (float)atoi(CTEXT(CDR(node)));
6231 // check his quadrants
6232 for(idx=0; idx<MAX_SHIELD_SECTIONS; idx++){
6233 if( ((objp->shields[idx] / max_quad) * 100.0f) <= check ){
6242 int sexp_secondary_ammo_pct(int node)
6251 sindex = ship_name_lookup(CTEXT(node));
6255 if((Ships[sindex].objnum < 0) || (Ships[sindex].objnum >= MAX_OBJECTS)){
6258 shipp = &Ships[sindex];
6261 check = atoi(CTEXT(CDR(node)));
6263 // bogus check? (3 == cumulative sum of all banks)
6264 if((check != 3) && (check > shipp->weapons.num_secondary_banks)){
6270 for(idx=0; idx<shipp->weapons.num_secondary_banks; idx++){
6271 ret_sum[idx] = (int)(((float)shipp->weapons.secondary_bank_ammo[idx] / (float)shipp->weapons.secondary_bank_start_ammo[idx]) * 100.0f);
6276 for(idx=0; idx<shipp->weapons.num_secondary_banks; idx++){
6277 ret += ret_sum[idx];
6279 ret = (int)((float)ret / (float)shipp->weapons.num_secondary_banks);
6281 ret = (int)(((float)shipp->weapons.secondary_bank_ammo[check] / (float)shipp->weapons.secondary_bank_start_ammo[check]) * 100.0f);
6288 void sexp_beam_fire(int node)
6291 beam_fire_info fire_info;
6295 memset(&fire_info, 0, sizeof(beam_fire_info));
6296 fire_info.accuracy = 0.000001f; // this will guarantee a hit
6298 // get the firing ship
6299 sindex = ship_name_lookup(CTEXT(node));
6303 if(Ships[sindex].objnum < 0){
6306 fire_info.shooter = &Objects[Ships[sindex].objnum];
6308 // get the subsystem
6309 fire_info.turret = ship_get_subsys(&Ships[sindex], CTEXT(CDR(node)));
6310 if(fire_info.turret == NULL){
6315 sindex = ship_name_lookup(CTEXT(CDR(CDR(node))));
6319 if(Ships[sindex].objnum < 0){
6322 fire_info.target = &Objects[Ships[sindex].objnum];
6324 // see if the optional subsystem can be found
6325 fire_info.target_subsys = NULL;
6326 fire_info.target_subsys = ship_get_subsys(&Ships[sindex], CTEXT(CDR(CDR(CDR(node)))));
6328 // if it has no primary weapons
6329 if(fire_info.turret->weapons.num_primary_banks <= 0){
6334 // if the turret is destroyed
6335 if(fire_info.turret->current_hits <= 0.0f){
6339 // hmm, this could be wacky. Let's just simply select the first beam weapon in the turret
6340 fire_info.beam_info_index = -1;
6341 for(idx=0; idx<fire_info.turret->weapons.num_primary_banks; idx++){
6342 // store the weapon info index
6343 if(Weapon_info[fire_info.turret->weapons.primary_bank_weapons[idx]].wi_flags & WIF_BEAM){
6344 fire_info.beam_info_index = fire_info.turret->weapons.primary_bank_weapons[idx];
6349 if(fire_info.beam_info_index != -1){
6350 beam_fire(&fire_info);
6352 // it would appear the turret doesn't have any beam weapons, dumbass
6357 void sexp_beam_free(int node)
6360 ship_subsys *turret = NULL;
6362 // get the firing ship
6363 sindex = ship_name_lookup(CTEXT(node));
6367 if(Ships[sindex].objnum < 0){
6373 // get the subsystem
6374 turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
6379 // flag it as beam free :)
6380 turret->weapons.flags |= SW_FLAG_BEAM_FREE;
6381 turret->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
6388 void sexp_beam_free_all(int node)
6390 ship_subsys *subsys;
6393 // get the firing ship
6394 sindex = ship_name_lookup(CTEXT(node));
6398 if(Ships[sindex].objnum < 0){
6402 // free all beam weapons
6403 subsys = GET_FIRST(&Ships[sindex].subsys_list);
6404 while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
6405 // just mark all turrets as beam free
6406 if(subsys->system_info->type == SUBSYSTEM_TURRET){
6407 subsys->weapons.flags |= SW_FLAG_BEAM_FREE;
6408 subsys->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
6412 subsys = GET_NEXT(subsys);
6416 void sexp_beam_lock(int node)
6419 ship_subsys *turret = NULL;
6421 // get the firing ship
6422 sindex = ship_name_lookup(CTEXT(node));
6426 if(Ships[sindex].objnum < 0){
6432 // get the subsystem
6433 turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
6438 // flag it as not beam free
6439 turret->weapons.flags &= ~(SW_FLAG_BEAM_FREE);
6446 void sexp_beam_lock_all(int node)
6448 ship_subsys *subsys;
6451 // get the firing ship
6452 sindex = ship_name_lookup(CTEXT(node));
6456 if(Ships[sindex].objnum < 0){
6460 // free all beam weapons
6461 subsys = GET_FIRST(&Ships[sindex].subsys_list);
6462 while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
6463 // just mark all turrets as not beam free
6464 if(subsys->system_info->type == SUBSYSTEM_TURRET){
6465 subsys->weapons.flags &= ~(SW_FLAG_BEAM_FREE);
6469 subsys = GET_NEXT(subsys);
6473 void sexp_turret_free(int node)
6476 ship_subsys *turret = NULL;
6478 // get the firing ship
6479 sindex = ship_name_lookup(CTEXT(node));
6483 if(Ships[sindex].objnum < 0){
6489 // get the subsystem
6490 turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
6495 // flag turret as no longer locked :)
6496 turret->weapons.flags &= (~SW_FLAG_TURRET_LOCK);
6497 turret->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
6504 void sexp_turret_free_all(int node)
6506 ship_subsys *subsys;
6509 // get the firing ship
6510 sindex = ship_name_lookup(CTEXT(node));
6514 if(Ships[sindex].objnum < 0){
6519 subsys = GET_FIRST(&Ships[sindex].subsys_list);
6520 while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
6521 // just mark all turrets as free
6522 if(subsys->system_info->type == SUBSYSTEM_TURRET){
6523 subsys->weapons.flags &= (~SW_FLAG_TURRET_LOCK);
6524 subsys->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
6528 subsys = GET_NEXT(subsys);
6532 void sexp_turret_lock(int node)
6535 ship_subsys *turret = NULL;
6537 // get the firing ship
6538 sindex = ship_name_lookup(CTEXT(node));
6542 if(Ships[sindex].objnum < 0){
6548 // get the subsystem
6549 turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
6554 // flag turret as locked
6555 turret->weapons.flags |= SW_FLAG_TURRET_LOCK;
6562 void sexp_turret_lock_all(int node)
6564 ship_subsys *subsys;
6567 // get the firing ship
6568 sindex = ship_name_lookup(CTEXT(node));
6572 if(Ships[sindex].objnum < 0){
6577 subsys = GET_FIRST(&Ships[sindex].subsys_list);
6578 while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
6579 // just mark all turrets as locked
6580 if(subsys->system_info->type == SUBSYSTEM_TURRET){
6581 subsys->weapons.flags |= SW_FLAG_TURRET_LOCK;
6585 subsys = GET_NEXT(subsys);
6589 void sexp_turret_tagged_only_all(int node)
6591 ship_subsys *subsys;
6594 // get the firing ship
6595 sindex = ship_name_lookup(CTEXT(node));
6599 if(Ships[sindex].objnum < 0){
6603 // mark all turrets to only target tagged ships
6604 subsys = GET_FIRST(&Ships[sindex].subsys_list);
6605 while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
6606 // just mark all turrets as locked
6607 if(subsys->system_info->type == SUBSYSTEM_TURRET){
6608 subsys->weapons.flags |= SW_FLAG_TAGGED_ONLY;
6612 subsys = GET_NEXT(subsys);
6616 void sexp_turret_tagged_clear_all(int node)
6618 ship_subsys *subsys;
6621 // get the firing ship
6622 sindex = ship_name_lookup(CTEXT(node));
6626 if(Ships[sindex].objnum < 0){
6630 // mark all turrets so not restricted to only tagged ships
6631 subsys = GET_FIRST(&Ships[sindex].subsys_list);
6632 while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
6633 // just mark all turrets as locked
6634 if(subsys->system_info->type == SUBSYSTEM_TURRET){
6635 subsys->weapons.flags &= (~SW_FLAG_TAGGED_ONLY);
6639 subsys = GET_NEXT(subsys);
6643 void sexp_add_remove_escort(int node)
6648 // get the firing ship
6649 sindex = ship_name_lookup(CTEXT(node));
6653 if(Ships[sindex].objnum < 0){
6657 // determine whether to add or remove it
6658 flag = atoi(CTEXT(CDR(node)));
6662 hud_add_ship_to_escort(Ships[sindex].objnum, 1);
6664 hud_remove_ship_from_escort(Ships[sindex].objnum);
6668 void sexp_awacs_set_radius(int node)
6673 // get the firing ship
6674 sindex = ship_name_lookup(CTEXT(node));
6678 if(Ships[sindex].objnum < 0){
6682 // get the awacs subsystem
6683 awacs = ship_get_subsys(&Ships[sindex], CTEXT(CDR(node)));
6688 // make sure this _is_ an awacs subsystem
6689 SDL_assert(awacs->system_info->flags & MSS_FLAG_AWACS);
6690 if(awacs->system_info->flags & MSS_FLAG_AWACS){
6694 // set the new awacs radius
6695 awacs->awacs_radius = (float)atoi(CTEXT(CDR(CDR(node))));
6698 int sexp_is_tagged(int node)
6702 // get the firing ship
6703 sindex = ship_name_lookup(CTEXT(node));
6707 if(Ships[sindex].objnum < 0){
6710 if(Ships[sindex].tag_left > 0.0f){
6718 int sexp_num_kills(int node)
6720 int sindex, np_index;
6723 // get the ship we're interested in
6724 sindex = ship_name_lookup(CTEXT(node));
6728 if(Ships[sindex].objnum < 0){
6732 // in multiplayer, search through all players
6733 if(Game_mode & GM_MULTIPLAYER){
6734 // try and find the player
6735 np_index = multi_find_player_by_object(&Objects[Ships[sindex].objnum]);
6736 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
6737 p = Net_players[np_index].player;
6740 // if we're in single player, we're only concerned with ourself
6743 if(Player_obj == &Objects[Ships[sindex].objnum]){
6748 // now, if we have a valid player, return his kills
6750 return p->stats.m_kill_count_ok;
6757 int sexp_num_type_kills(int node)
6759 int sindex, np_index, st_index;
6763 // get the ship we're interested in
6764 sindex = ship_name_lookup(CTEXT(node));
6768 if(Ships[sindex].objnum < 0){
6772 // in multiplayer, search through all players
6773 if(Game_mode & GM_MULTIPLAYER){
6774 // try and find the player
6775 np_index = multi_find_player_by_object(&Objects[Ships[sindex].objnum]);
6776 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
6777 p = Net_players[np_index].player;
6780 // if we're in single player, we're only concerned with ourself
6783 if(Player_obj == &Objects[Ships[sindex].objnum]){
6793 // lookup ship type name
6794 st_index = ship_type_name_lookup(CTEXT(CDR(node)));
6801 for(idx=0; idx<Num_ship_types; idx++){
6802 if((p->stats.m_okKills[idx] > 0) && (Ship_info[idx].flags & Ship_type_flags[st_index])){
6803 total += p->stats.m_okKills[idx];
6811 int sexp_num_class_kills(int node)
6813 int sindex, np_index, si_index;
6816 // get the ship we're interested in
6817 sindex = ship_name_lookup(CTEXT(node));
6821 if(Ships[sindex].objnum < 0){
6825 // in multiplayer, search through all players
6826 if(Game_mode & GM_MULTIPLAYER){
6827 // try and find the player
6828 np_index = multi_find_player_by_object(&Objects[Ships[sindex].objnum]);
6829 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
6830 p = Net_players[np_index].player;
6833 // if we're in single player, we're only concerned with ourself
6836 if(Player_obj == &Objects[Ships[sindex].objnum]){
6846 // get the ship type we're looking for
6847 si_index = ship_info_lookup(CTEXT(CDR(node)));
6848 if((si_index < 0) || (si_index > Num_ship_types)){
6853 return p->stats.m_okKills[si_index];
6856 void sexp_subsys_set_random(int node)
6858 int sindex, low, high, n, idx, rand, exclusion_list[MAX_MODEL_SUBSYSTEMS];
6859 ship_subsys *subsys;
6863 sindex = ship_name_lookup(CTEXT(node));
6867 if(Ships[sindex].objnum < 0){
6870 shipp = &Ships[sindex];
6873 low = num_eval(CDR(node));
6879 high = num_eval(CDR(CDR(node)));
6889 n = CDR(CDR(CDR(node)));
6891 // init exclusion list
6892 memset(exclusion_list, 0, sizeof(int) * Ship_info[shipp->ship_info_index].n_subsystems);
6894 // get exclusion list
6896 int exclude_index = ship_get_subsys_index(shipp, CTEXT(n), 0);
6897 if (exclude_index >= 0) {
6898 exclusion_list[exclude_index] = 1;
6904 // apply to all others
6905 for (idx=0; idx<Ship_info[shipp->ship_info_index].n_subsystems; idx++) {
6906 if ( exclusion_list[idx] == 0 ) {
6907 // get non excluded subsystem
6908 subsys = ship_get_indexed_subsys(shipp, idx, NULL);
6910 // randomize its hit points
6911 rand = rand_internal(low, high);
6912 subsys->current_hits = 0.01f * rand * subsys->system_info->max_hits;
6917 void sexp_supernova_start(int node)
6919 supernova_start(atoi(CTEXT(node)));
6922 int sexp_is_secondary_selected(int node)
6929 sindex = ship_name_lookup(CTEXT(node));
6933 if(Ships[sindex].objnum < 0){
6936 shipp = &Ships[sindex];
6939 bank = atoi(CTEXT(CDR(node)));
6940 if(bank >= shipp->weapons.num_secondary_banks){
6944 // is this the bank currently selected
6945 if(bank == shipp->weapons.current_secondary_bank){
6953 int sexp_is_primary_selected(int node)
6960 sindex = ship_name_lookup(CTEXT(node));
6964 if(Ships[sindex].objnum < 0){
6967 shipp = &Ships[sindex];
6970 bank = atoi(CTEXT(CDR(node)));
6971 if(bank >= shipp->weapons.num_primary_banks){
6975 // is this the bank currently selected
6976 if(bank == shipp->weapons.current_primary_bank){
6984 #define RIGHT_QUAD 0
6985 #define FRONT_QUAD 1
6989 // Return SEXP_TRUE if quadrant quadnum is near max.
6990 int shield_quad_near_max(int quadnum)
6992 float remaining = 0.0f;
6993 for (int i=0; i<MAX_SHIELD_SECTIONS; i++) {
6997 remaining += Player_obj->shields[i];
7000 if ((remaining < 2.0f) || (Player_obj->shields[quadnum] > Ship_info[Player_ship->ship_info_index].shields/MAX_SHIELD_SECTIONS - 5.0f)) {
7007 // Return truth value for special SEXP.
7008 // Used in training#5, perhaps in other missions.
7009 int process_special_sexps(int index)
7012 case 0: // Ship "Freighter 1" is aspect locked by player.
7013 if (Player_ai->target_objnum != -1) {
7014 if (!(SDL_strcasecmp(Ships[Objects[Player_ai->target_objnum].instance].ship_name, "Freighter 1"))) {
7015 if (Player_ai->current_target_is_locked)
7021 case 1: // Fired Interceptors
7023 for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
7024 if (objp->type == OBJ_WEAPON) {
7025 if (!SDL_strcasecmp(Weapon_info[Weapons[objp->instance].weapon_info_index].name, "Interceptor#weak")) {
7026 int target = Weapons[objp->instance].target_num;
7028 if (Objects[target].type == OBJ_SHIP) {
7029 if (!(SDL_strcasecmp(Ships[Objects[target].instance].ship_name, "Freighter 1")))
7037 case 2: // Ship "Freighter 1", subsystem "Weapons" is aspect locked by player.
7038 if (Player_ai->target_objnum != -1) {
7039 if (!(SDL_strcasecmp(Ships[Objects[Player_ai->target_objnum].instance].ship_name, "Freighter 1"))) {
7040 if (!(SDL_strcasecmp(Player_ai->targeted_subsys->system_info->name, "Weapons"))) {
7041 if (Player_ai->current_target_is_locked){
7049 case 3: // Player ship suffering shield damage on front.
7050 apply_damage_to_shield(Player_obj, FRONT_QUAD, 10.0f);
7051 hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
7054 case 4: // Player ship suffering much damage.
7055 nprintf(("AI", "Frame %i\n", Framecount));
7056 apply_damage_to_shield(Player_obj, FRONT_QUAD, 10.0f);
7057 hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
7058 if (Player_obj->shields[FRONT_QUAD] < 2.0f)
7063 case 5: // Player's shield is quick repaired
7064 nprintf(("AI", "Frame %i, recharged to %7.3f\n", Framecount, Player_obj->shields[FRONT_QUAD]));
7066 apply_damage_to_shield(Player_obj, FRONT_QUAD, -flFrametime*200.0f);
7068 if (Player_obj->shields[FRONT_QUAD] > Ship_info[Player_ship->ship_info_index].shields/4.0f)
7069 Player_obj->shields[FRONT_QUAD] = Ship_info[Player_ship->ship_info_index].shields/4.0f;
7071 //hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
7072 if (Player_obj->shields[FRONT_QUAD] > Player_obj->shields[(FRONT_QUAD+1)%4] - 2.0f)
7077 case 6: // 3 of player's shield quadrants are reduced to 0.
7078 Player_obj->shields[1] = 1.0f;
7079 Player_obj->shields[2] = 1.0f;
7080 Player_obj->shields[3] = 1.0f;
7081 //apply_damage_to_shield(Player_obj, FRONT_QUAD, 1.0f);
7082 hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
7084 case 7: // Make sure front quadrant has been maximized, or close to it.
7085 if (shield_quad_near_max(FRONT_QUAD)) return SEXP_TRUE; else return SEXP_FALSE;
7088 case 8: // Make sure rear quadrant has been maximized, or close to it.
7089 if (shield_quad_near_max(REAR_QUAD)) return SEXP_TRUE; else return SEXP_FALSE;
7092 case 9: // Zero left and right quadrants in preparation for maximizing rear quadrant.
7093 Player_obj->shields[LEFT_QUAD] = 0.0f;
7094 Player_obj->shields[RIGHT_QUAD] = 0.0f;
7095 hud_shield_quadrant_hit(Player_obj, LEFT_QUAD);
7099 case 10: // Return true if player is low on Interceptors.
7100 if (Player_ship->weapons.secondary_bank_ammo[0] + Player_ship->weapons.secondary_bank_ammo[1] < 8)
7106 case 11: // Return true if player has plenty of Interceptors.
7107 if (Player_ship->weapons.secondary_bank_ammo[0] + Player_ship->weapons.secondary_bank_ammo[1] >= 8)
7113 case 12: // Return true if player is low on Interceptors.
7114 if (Player_ship->weapons.secondary_bank_ammo[0] + Player_ship->weapons.secondary_bank_ammo[1] < 4)
7120 case 13: // Zero front shield quadrant. Added for Jim Boone on August 26, 1999 by MK.
7121 Player_obj->shields[FRONT_QUAD] = 0.0f;
7122 hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
7126 case 100: // Return true if player is out of countermeasures.
7127 if (Player_ship->cmeasure_count <= 0)
7133 Int3(); // Unsupported node type.
7139 // custom sexp operator for handling misc training stuff
7140 int sexp_special_training_check(int node)
7144 num = atoi(CTEXT(node));
7145 if (num == SPECIAL_CHECK_TRAINING_FAILURE)
7146 return Training_failure ? SEXP_TRUE : SEXP_FALSE;
7148 // To MK: do whatever you want with this number here.
7149 rtn = process_special_sexps(atoi(CTEXT(node)));
7154 // sexpression to flash a hud gauge. gauge name is text valud of node
7155 void sexp_flash_hud_gauge( int node )
7161 for (i = 0; i < NUM_HUD_GAUGES; i++ ) {
7162 if ( !SDL_strcasecmp(HUD_gauge_text[i], name) ) {
7163 hud_gauge_start_flash(i); // call HUD function to flash gauge
7169 void sexp_set_training_context_fly_path(int node)
7173 for (i=0; i<Num_waypoint_lists; i++)
7174 if (!SDL_strcasecmp(CTEXT(node), Waypoint_lists[i].name))
7177 if (i < Num_waypoint_lists) {
7178 Training_context |= TRAINING_CONTEXT_FLY_PATH;
7179 Training_context_path = i;
7180 Training_context_distance = (float) atof(CTEXT(CDR(node)));
7181 Training_context_goal_waypoint = 0;
7182 Training_context_at_waypoint = -1;
7186 void sexp_set_training_context_speed(int node)
7188 Training_context |= TRAINING_CONTEXT_SPEED;
7189 Training_context_speed_min = atoi(CTEXT(node));
7190 Training_context_speed_max = atoi(CTEXT(CDR(node)));
7191 Training_context_speed_set = 0;
7194 // high-level sexpression evaluator
7195 int eval_sexp(int cur_node)
7197 int node, sexp_val = UNINITIALIZED;
7198 if (cur_node == -1) // empty list, i.e. sexp: ( )
7201 SDL_assert(cur_node >= 0); // we have special sexp nodes <= -1!!! MWA
7202 // which should be intercepted before we get here. HOFFOSS
7204 SDL_assert( (SEXP_NODE_TYPE(cur_node) == SEXP_LIST) || (SEXP_NODE_TYPE(cur_node) == SEXP_ATOM) );
7206 // trap known true and known false sexpressions. We don't trap on SEXP_NAN sexpressions since
7207 // they may yet evaluate to true or false.
7209 if (Sexp_nodes[cur_node].value == SEXP_KNOWN_TRUE)
7211 else if (Sexp_nodes[cur_node].value == SEXP_KNOWN_FALSE)
7214 if (Sexp_nodes[cur_node].first != -1) {
7215 node = CAR(cur_node);
7216 sexp_val = eval_sexp(node);
7217 Sexp_nodes[cur_node].value = Sexp_nodes[node].value; // higher level node gets node value
7223 node = CDR(cur_node); // makes reading the next bit of code a little easier.
7225 op_num = find_operator(CTEXT(cur_node));
7227 // arithmetic operators will always return just their value
7229 sexp_val = add_sexps( node );
7233 sexp_val = sub_sexps( node );
7237 sexp_val = mul_sexps( node );
7241 sexp_val = mod_sexps( node );
7245 sexp_val = div_sexps( node );
7249 sexp_val = rand_sexp( node );
7252 // boolean operators can have one of the special sexp values (known true, known false, unknown)
7254 sexp_val = SEXP_KNOWN_TRUE;
7258 sexp_val = SEXP_KNOWN_FALSE;
7262 sexp_val = sexp_or( node );
7266 sexp_val = sexp_and( node );
7269 case OP_AND_IN_SEQUENCE:
7270 sexp_val = sexp_and_in_sequence( node );
7273 case OP_GREATER_THAN:
7274 sexp_val = sexp_gt( node );
7278 sexp_val = sexp_lt( node );
7282 sexp_val = sexp_equal( node );
7286 sexp_val = sexp_is_iff( node );
7290 sexp_val = sexp_not( node );
7293 case OP_PREVIOUS_GOAL_TRUE:
7294 sexp_val = sexp_previous_goal_status( node, GOAL_COMPLETE );
7297 case OP_PREVIOUS_GOAL_FALSE:
7298 sexp_val = sexp_previous_goal_status( node, GOAL_FAILED );
7301 case OP_PREVIOUS_GOAL_INCOMPLETE:
7302 sexp_val = sexp_previous_goal_status( node, GOAL_INCOMPLETE );
7305 case OP_PREVIOUS_EVENT_TRUE:
7306 sexp_val = sexp_previous_event_status( node, EVENT_SATISFIED );
7309 case OP_PREVIOUS_EVENT_FALSE:
7310 sexp_val = sexp_previous_event_status( node, EVENT_FAILED );
7313 case OP_PREVIOUS_EVENT_INCOMPLETE:
7314 sexp_val = sexp_previous_event_status( node, EVENT_INCOMPLETE );
7318 case OP_EVENT_FALSE:
7319 sexp_val = sexp_event_status( node, (op_num == OP_EVENT_TRUE?1:0) );
7320 if ((sexp_val != SEXP_TRUE) && (sexp_val != SEXP_KNOWN_TRUE))
7321 Sexp_useful_number = 0; // indicate sexp isn't current yet
7325 case OP_EVENT_TRUE_DELAY:
7326 case OP_EVENT_FALSE_DELAY:
7327 sexp_val = sexp_event_delay_status( node, (op_num == OP_EVENT_TRUE_DELAY?1:0) );
7328 if ((sexp_val != SEXP_TRUE) && (sexp_val != SEXP_KNOWN_TRUE))
7329 Sexp_useful_number = 0; // indicate sexp isn't current yet
7332 case OP_GOAL_TRUE_DELAY:
7333 case OP_GOAL_FALSE_DELAY:
7334 sexp_val = sexp_goal_delay_status( node, (op_num == OP_GOAL_TRUE_DELAY?1:0) );
7337 case OP_EVENT_INCOMPLETE:
7338 sexp_val = sexp_event_incomplete( node );
7339 if ((sexp_val != SEXP_TRUE) && (sexp_val != SEXP_KNOWN_TRUE))
7340 Sexp_useful_number = 0; // indicate sexp isn't current yet
7343 case OP_GOAL_INCOMPLETE:
7344 sexp_val = sexp_goal_incomplete( node );
7347 // destroy type sexpressions
7348 case OP_IS_DESTROYED:
7349 sexp_val = sexp_is_destroyed( node, NULL );
7352 case OP_IS_SUBSYSTEM_DESTROYED:
7353 sexp_val = sexp_is_subsystem_destroyed( node );
7357 sexp_val = sexp_has_docked( node );
7360 case OP_HAS_ARRIVED:
7361 sexp_val = sexp_has_arrived( node, NULL );
7364 case OP_HAS_DEPARTED:
7365 sexp_val = sexp_has_departed( node, NULL );
7368 case OP_HAS_UNDOCKED:
7369 sexp_val = sexp_has_undocked( node );
7372 case OP_IS_DISABLED:
7373 sexp_val = sexp_is_disabled( node, NULL );
7376 case OP_IS_DISARMED:
7377 sexp_val = sexp_is_disarmed( node, NULL );
7380 case OP_WAYPOINTS_DONE:
7381 sexp_val = sexp_are_waypoints_done( node );
7384 // objective operators that use a delay
7385 case OP_IS_DESTROYED_DELAY:
7386 sexp_val = sexp_is_destroyed_delay( node );
7389 case OP_IS_SUBSYSTEM_DESTROYED_DELAY:
7390 sexp_val = sexp_is_subsystem_destroyed_delay( node );
7393 case OP_HAS_DOCKED_DELAY:
7394 sexp_val = sexp_has_docked_delay( node );
7397 case OP_HAS_ARRIVED_DELAY:
7398 sexp_val = sexp_has_arrived_delay( node );
7401 case OP_HAS_DEPARTED_DELAY:
7402 sexp_val = sexp_has_departed_delay( node );
7405 case OP_HAS_UNDOCKED_DELAY:
7406 sexp_val = sexp_has_undocked_delay( node );
7409 case OP_IS_DISABLED_DELAY:
7410 sexp_val = sexp_is_disabled_delay( node );
7413 case OP_IS_DISARMED_DELAY:
7414 sexp_val = sexp_is_disarmed_delay( node );
7417 case OP_WAYPOINTS_DONE_DELAY:
7418 sexp_val = sexp_are_waypoints_done_delay( node );
7421 case OP_SHIP_TYPE_DESTROYED:
7422 sexp_val = sexp_ship_type_destroyed( node );
7425 // time based sexpressions
7426 case OP_HAS_TIME_ELAPSED:
7427 sexp_val = sexp_has_time_elapsed( node );
7430 case OP_MODIFY_VARIABLE:
7431 sexp_modify_variable( node );
7432 sexp_val = 1; // 1 means only do once.
7435 case OP_TIME_SHIP_DESTROYED:
7436 sexp_val = sexp_time_destroyed( node );
7439 case OP_TIME_WING_DESTROYED:
7440 sexp_val = sexp_time_wing_destroyed( node );
7443 case OP_TIME_SHIP_ARRIVED:
7444 sexp_val = sexp_time_ship_arrived( node );
7447 case OP_TIME_WING_ARRIVED:
7448 sexp_val = sexp_time_wing_arrived( node );
7451 case OP_TIME_SHIP_DEPARTED:
7452 sexp_val = sexp_time_ship_departed( node );
7455 case OP_TIME_WING_DEPARTED:
7456 sexp_val = sexp_time_wing_departed( node );
7459 case OP_MISSION_TIME:
7460 sexp_val = sexp_mission_time();
7463 case OP_TIME_DOCKED:
7464 sexp_val = sexp_time_docked( node );
7467 case OP_TIME_UNDOCKED:
7468 sexp_val = sexp_time_undocked( node );
7471 // info based sexpressions (like shields, hits
7472 case OP_SHIELDS_LEFT:
7473 sexp_val = sexp_shields_left( node );
7477 sexp_val = sexp_hits_left( node );
7480 case OP_SPECIAL_WARP_DISTANCE:
7481 sexp_val = sexp_special_warp_dist( node );
7484 case OP_HITS_LEFT_SUBSYSTEM:
7485 sexp_val = sexp_hits_left_subsystem( node );
7489 sexp_val = sexp_distance( node );
7492 case OP_IS_SHIP_VISIBLE:
7493 sexp_val = sexp_is_ship_visible( node );
7497 sexp_val = sexp_team_score( node );
7500 case OP_LAST_ORDER_TIME:
7501 sexp_val = sexp_last_order_time( node );
7504 case OP_NUM_PLAYERS:
7505 sexp_val = sexp_num_players();
7508 case OP_SKILL_LEVEL_AT_LEAST:
7509 sexp_val = sexp_skill_level_at_least( node );
7512 case OP_IS_CARGO_KNOWN:
7513 case OP_CARGO_KNOWN_DELAY:
7514 sexp_val = sexp_is_cargo_known( node, (op_num==OP_IS_CARGO_KNOWN)?0:1 );
7517 case OP_HAS_BEEN_TAGGED_DELAY:
7518 sexp_val = sexp_has_been_tagged_delay(node);
7521 case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
7522 sexp_val = sexp_cap_subsys_cargo_known_delay(node);
7525 case OP_WAS_PROMOTION_GRANTED:
7526 sexp_val = sexp_was_promotion_granted(node);
7529 case OP_WAS_MEDAL_GRANTED:
7530 sexp_val = sexp_was_medal_granted(node);
7533 case OP_PERCENT_SHIPS_DEPARTED:
7534 case OP_PERCENT_SHIPS_DESTROYED:
7535 sexp_val = sexp_percent_ships_depart_destroy(node, op_num);
7538 case OP_DEPART_NODE_DELAY:
7539 sexp_val = sexp_depart_node_delay( node );
7542 case OP_DESTROYED_DEPARTED_DELAY:
7543 sexp_val = sexp_destroyed_departed_delay( node );
7546 // conditional sexpressions
7548 sexp_val = eval_when( node );
7552 sexp_val = eval_cond( node );
7555 // sexpressions with side effects
7557 sexp_change_iff( node );
7561 case OP_ADD_SHIP_GOAL:
7562 sexp_add_ship_goal( node );
7566 case OP_ADD_WING_GOAL:
7567 sexp_add_wing_goal( node );
7572 sexp_add_goal( node );
7576 case OP_CLEAR_SHIP_GOALS:
7577 sexp_clear_ship_goals( node );
7581 case OP_CLEAR_WING_GOALS:
7582 sexp_clear_wing_goals( node );
7586 case OP_CLEAR_GOALS:
7587 sexp_clear_goals( node );
7591 case OP_PROTECT_SHIP:
7592 case OP_UNPROTECT_SHIP:
7593 sexp_protect_ships( node, (op_num==OP_PROTECT_SHIP?1:0) );
7597 case OP_BEAM_PROTECT_SHIP:
7598 case OP_BEAM_UNPROTECT_SHIP:
7599 sexp_beam_protect_ships( node, (op_num==OP_BEAM_PROTECT_SHIP?1:0) );
7603 case OP_SHIP_INVISIBLE:
7604 case OP_SHIP_VISIBLE:
7605 sexp_ships_visible( node, (op_num==OP_SHIP_VISIBLE?1:0) );
7609 case OP_SHIP_VULNERABLE:
7610 case OP_SHIP_INVULNERABLE:
7611 sexp_ships_invulnerable( node, (op_num==OP_SHIP_INVULNERABLE?1:0) );
7615 case OP_SHIP_GUARDIAN:
7616 case OP_SHIP_NO_GUARDIAN:
7617 sexp_ships_guardian( node, (op_num==OP_SHIP_GUARDIAN?1:0) );
7621 case OP_SHIP_VANISH:
7622 sexp_ship_vanish( node );
7626 case OP_SEND_MESSAGE:
7627 sexp_send_message( node );
7631 case OP_SEND_MESSAGE_LIST:
7632 sexp_send_message_list( node );
7636 case OP_SEND_RANDOM_MESSAGE:
7637 sexp_send_random_message( node );
7641 case OP_SELF_DESTRUCT:
7642 sexp_self_destruct( node );
7646 case OP_NEXT_MISSION:
7647 sexp_next_mission( node );
7651 case OP_END_OF_CAMPAIGN:
7652 sexp_end_of_campaign( node );
7656 case OP_END_CAMPAIGN:
7657 sexp_end_campaign( node );
7661 case OP_SABOTAGE_SUBSYSTEM:
7662 sexp_sabotage_subsystem( node );
7666 case OP_REPAIR_SUBSYSTEM:
7667 sexp_repair_subsystem( node );
7671 case OP_SET_SUBSYSTEM_STRNGTH:
7672 sexp_set_subsystem_strength( node );
7676 case OP_INVALIDATE_GOAL:
7677 case OP_VALIDATE_GOAL:
7678 sexp_change_goal_validity( node, (op_num==OP_INVALIDATE_GOAL?0:1) );
7682 case OP_TRANSFER_CARGO:
7683 sexp_transfer_cargo( node );
7687 case OP_EXCHANGE_CARGO:
7688 sexp_exchange_cargo( node );
7693 case OP_JETTISON_CARGO:
7694 sexp_jettison_cargo( node );
7698 case OP_CARGO_NO_DEPLETE:
7699 sexp_cargo_no_deplete( node );
7703 case OP_SET_SPECIAL_WARPOUT_NAME:
7704 sexp_special_warpout_name( node );
7708 case OP_END_MISSION_DELAY:
7709 sexp_end_mission_delay( node );
7713 // sexpressions for setting flag for good/bad time for someone to reasm
7714 case OP_GOOD_REARM_TIME:
7715 sexp_good_time_to_rearm( node );
7719 case OP_GRANT_PROMOTION:
7720 sexp_grant_promotion();
7724 case OP_GRANT_MEDAL:
7725 sexp_grant_medal( node );
7729 case OP_WARP_BROKEN:
7730 case OP_WARP_NOT_BROKEN:
7731 sexp_deal_with_warp( node, 0, op_num==OP_WARP_BROKEN?1:0 );
7736 case OP_WARP_ALLOWED:
7737 sexp_deal_with_warp( node, 1, op_num==OP_WARP_NEVER?1:0 );
7741 case OP_GOOD_SECONDARY_TIME:
7742 sexp_good_secondary_time( node );
7746 // sexpressions to allow shpis/weapons during the course of a mission
7748 sexp_allow_ship( node );
7752 case OP_ALLOW_WEAPON:
7753 sexp_allow_weapon( node );
7757 case OP_TECH_ADD_SHIP:
7758 sexp_tech_add_ship(node);
7762 case OP_TECH_ADD_WEAPON:
7763 sexp_tech_add_weapon(node);
7767 // in the case of a red_alert mission, simply call the red alert function to close
7768 // the current campaign's mission and move forward to the next mission
7770 red_alert_start_mission();
7774 // training operators
7775 case OP_KEY_PRESSED:
7776 sexp_val = sexp_key_pressed(node);
7779 case OP_SPECIAL_CHECK:
7780 sexp_val = sexp_special_training_check(node);
7784 sexp_key_reset(node);
7785 sexp_val = SEXP_KNOWN_TRUE; // only do it first time in repeating events.
7789 sexp_val = sexp_targeted(node);
7793 sexp_val = sexp_speed(node);
7796 case OP_SECONDARIES_DEPLETED:
7797 sexp_val = sexp_secondaries_depleted(node);
7801 sexp_val = sexp_facing(node);
7805 sexp_val = sexp_facing2(node);
7809 sexp_val = sexp_order(node);
7812 case OP_WAYPOINT_MISSED:
7813 sexp_val = sexp_waypoint_missed();
7816 case OP_WAYPOINT_TWICE:
7817 sexp_val = sexp_waypoint_twice();
7821 sexp_val = sexp_path_flown();
7824 case OP_TRAINING_MSG:
7825 sexp_send_training_message(node);
7829 case OP_FLASH_HUD_GAUGE:
7830 sexp_flash_hud_gauge(node);
7834 case OP_SET_TRAINING_CONTEXT_FLY_PATH:
7835 sexp_set_training_context_fly_path(node);
7839 case OP_SET_TRAINING_CONTEXT_SPEED:
7840 sexp_set_training_context_speed(node);
7844 // debugging operators
7850 case 0: // zero represents a non-operator
7851 return atoi(CTEXT(cur_node));
7858 sexp_beam_fire(node);
7863 sexp_val = sexp_is_tagged(node);
7867 sexp_val = sexp_num_kills(node);
7870 case OP_NUM_TYPE_KILLS:
7871 sexp_val = sexp_num_type_kills(node);
7874 case OP_NUM_CLASS_KILLS:
7875 sexp_val = sexp_num_class_kills(node);
7880 sexp_beam_free(node);
7883 case OP_BEAM_FREE_ALL:
7885 sexp_beam_free_all(node);
7890 sexp_beam_lock(node);
7893 case OP_BEAM_LOCK_ALL:
7895 sexp_beam_lock_all(node);
7898 case OP_TURRET_FREE:
7900 sexp_turret_free(node);
7903 case OP_TURRET_FREE_ALL:
7905 sexp_turret_free_all(node);
7908 case OP_TURRET_LOCK:
7910 sexp_turret_lock(node);
7913 case OP_TURRET_LOCK_ALL:
7915 sexp_turret_lock_all(node);
7918 case OP_ADD_REMOVE_ESCORT:
7920 sexp_add_remove_escort(node);
7923 case OP_AWACS_SET_RADIUS:
7925 sexp_awacs_set_radius(node);
7928 case OP_CAP_WAYPOINT_SPEED:
7930 sexp_cap_waypont_speed(node);
7933 case OP_TURRET_TAGGED_ONLY_ALL:
7935 sexp_turret_tagged_only_all(node);
7938 case OP_TURRET_TAGGED_CLEAR_ALL:
7940 sexp_turret_tagged_clear_all(node);
7943 case OP_SUBSYS_SET_RANDOM:
7945 sexp_subsys_set_random(node);
7948 case OP_SUPERNOVA_START:
7950 sexp_supernova_start(node);
7953 case OP_SHIELD_RECHARGE_PCT:
7954 sexp_val = sexp_shield_recharge_pct(node);
7957 case OP_ENGINE_RECHARGE_PCT:
7958 sexp_val = sexp_engine_recharge_pct(node);
7961 case OP_WEAPON_RECHARGE_PCT:
7962 sexp_val = sexp_weapon_recharge_pct(node);
7965 case OP_SHIELD_QUAD_LOW:
7966 sexp_val = sexp_shield_quad_low(node);
7969 case OP_SECONDARY_AMMO_PCT:
7970 sexp_val = sexp_secondary_ammo_pct(node);
7973 case OP_IS_SECONDARY_SELECTED:
7974 sexp_val = sexp_is_secondary_selected(node);
7977 case OP_IS_PRIMARY_SELECTED:
7978 sexp_val = sexp_is_primary_selected(node);
7982 Error(LOCATION, "Looking for SEXP operator, found '%s'.\n", CTEXT(cur_node));
7986 SDL_assert(sexp_val != UNINITIALIZED);
7988 // if we haven't returned, check the sexp value of the sexpression evaluation. A special
7989 // value of known true or known false means that we should set the sexp.value field for
7990 // short circuit eval (and return that special value as well).
7991 if (sexp_val == SEXP_KNOWN_TRUE) {
7992 Sexp_nodes[cur_node].value = SEXP_KNOWN_TRUE;
7996 if (sexp_val == SEXP_KNOWN_FALSE) {
7997 Sexp_nodes[cur_node].value = SEXP_KNOWN_FALSE;
8001 if ( sexp_val == SEXP_NAN ) {
8002 Sexp_nodes[cur_node].value = SEXP_NAN; // not a number values are false I would suspect
8006 if ( sexp_val == SEXP_NAN_FOREVER ) {
8007 Sexp_nodes[cur_node].value = SEXP_NAN_FOREVER;
8011 if ( sexp_val == SEXP_CANT_EVAL ) {
8012 Sexp_nodes[cur_node].value = SEXP_CANT_EVAL;
8013 Sexp_useful_number = 0; // indicate sexp isn't current yet
8017 if ( Sexp_nodes[cur_node].value == SEXP_NAN ) { // if we had a nan, but now don't, reset the value
8018 Sexp_nodes[cur_node].value = SEXP_UNKNOWN;
8023 Sexp_nodes[cur_node].value = SEXP_TRUE;
8025 Sexp_nodes[cur_node].value = SEXP_FALSE;
8032 // Still a debug-level system.
8033 // get_sexp_main reads and builds the internal representation for a
8034 // symbolic expression.
8036 // Mp points at first character in expression.
8037 // The symbolic expression is built in Sexp_nodes beginning at node 0.
8041 char token[TOKEN_LENGTH];
8044 ignore_white_space();
8047 if (!strncmp(Mp, "( )", 3))
8050 SDL_assert(*Mp == '(');
8052 start_node = get_sexp(token);
8053 // only need to check syntax if we have a operator
8054 if ( /*Sexp_nodes[start_node].subtype != SEXP_ATOM_OPERATOR ||*/ Fred_running || (start_node == -1))
8060 op = identify_operator(CTEXT(start_node));
8062 Error (LOCATION, "Can't find operator %s in operator list\n.", CTEXT(start_node) );
8072 while (*Mp != '#') {
8074 diag_printf("\n----------------\n");
8075 ignore_white_space();
8080 // returns the data type returned by an operator
8081 int query_operator_return_type(int op)
8085 SDL_assert(op >= 0 && op < Num_operators);
8086 op = Operators[op].value;
8094 case OP_AND_IN_SEQUENCE:
8098 case OP_GREATER_THAN:
8100 case OP_IS_DESTROYED:
8101 case OP_IS_SUBSYSTEM_DESTROYED:
8102 case OP_IS_DISABLED:
8103 case OP_IS_DISARMED:
8105 case OP_HAS_UNDOCKED:
8106 case OP_HAS_ARRIVED:
8107 case OP_HAS_DEPARTED:
8108 case OP_IS_DESTROYED_DELAY:
8109 case OP_IS_SUBSYSTEM_DESTROYED_DELAY:
8110 case OP_IS_DISABLED_DELAY:
8111 case OP_IS_DISARMED_DELAY:
8112 case OP_HAS_DOCKED_DELAY:
8113 case OP_HAS_UNDOCKED_DELAY:
8114 case OP_HAS_ARRIVED_DELAY:
8115 case OP_HAS_DEPARTED_DELAY:
8117 case OP_HAS_TIME_ELAPSED:
8118 case OP_GOAL_INCOMPLETE:
8119 case OP_GOAL_TRUE_DELAY:
8120 case OP_GOAL_FALSE_DELAY:
8121 case OP_EVENT_INCOMPLETE:
8122 case OP_EVENT_TRUE_DELAY:
8123 case OP_EVENT_FALSE_DELAY:
8124 case OP_PREVIOUS_EVENT_TRUE:
8125 case OP_PREVIOUS_EVENT_FALSE:
8126 case OP_PREVIOUS_EVENT_INCOMPLETE:
8127 case OP_PREVIOUS_GOAL_TRUE:
8128 case OP_PREVIOUS_GOAL_FALSE:
8129 case OP_PREVIOUS_GOAL_INCOMPLETE:
8130 case OP_WAYPOINTS_DONE:
8131 case OP_WAYPOINTS_DONE_DELAY:
8132 case OP_SHIP_TYPE_DESTROYED:
8133 case OP_LAST_ORDER_TIME:
8134 case OP_KEY_PRESSED:
8140 case OP_WAYPOINT_MISSED:
8141 case OP_WAYPOINT_TWICE:
8144 case OP_EVENT_FALSE:
8145 case OP_SKILL_LEVEL_AT_LEAST:
8146 case OP_IS_CARGO_KNOWN:
8147 case OP_HAS_BEEN_TAGGED_DELAY:
8148 case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
8149 case OP_CARGO_KNOWN_DELAY:
8150 case OP_WAS_PROMOTION_GRANTED:
8151 case OP_WAS_MEDAL_GRANTED:
8152 case OP_PERCENT_SHIPS_DEPARTED:
8153 case OP_PERCENT_SHIPS_DESTROYED:
8154 case OP_DEPART_NODE_DELAY:
8155 case OP_DESTROYED_DEPARTED_DELAY:
8156 case OP_SPECIAL_CHECK:
8158 case OP_SECONDARIES_DEPLETED:
8159 case OP_SHIELD_QUAD_LOW:
8160 case OP_IS_SECONDARY_SELECTED:
8161 case OP_IS_PRIMARY_SELECTED:
8170 case OP_TIME_SHIP_DESTROYED:
8171 case OP_TIME_SHIP_ARRIVED:
8172 case OP_TIME_SHIP_DEPARTED:
8173 case OP_TIME_WING_DESTROYED:
8174 case OP_TIME_WING_ARRIVED:
8175 case OP_TIME_WING_DEPARTED:
8176 case OP_MISSION_TIME:
8177 case OP_TIME_DOCKED:
8178 case OP_TIME_UNDOCKED:
8179 case OP_SHIELDS_LEFT:
8181 case OP_HITS_LEFT_SUBSYSTEM:
8183 case OP_NUM_PLAYERS:
8185 case OP_NUM_TYPE_KILLS:
8186 case OP_NUM_CLASS_KILLS:
8187 case OP_SHIELD_RECHARGE_PCT:
8188 case OP_ENGINE_RECHARGE_PCT:
8189 case OP_WEAPON_RECHARGE_PCT:
8190 case OP_SECONDARY_AMMO_PCT:
8191 case OP_SPECIAL_WARP_DISTANCE:
8192 case OP_IS_SHIP_VISIBLE:
8194 return OPR_POSITIVE;
8199 case OP_CLEAR_SHIP_GOALS:
8200 case OP_CLEAR_WING_GOALS:
8201 case OP_CLEAR_GOALS:
8202 case OP_ADD_SHIP_GOAL:
8203 case OP_ADD_WING_GOAL:
8205 case OP_PROTECT_SHIP:
8206 case OP_UNPROTECT_SHIP:
8207 case OP_BEAM_PROTECT_SHIP:
8208 case OP_BEAM_UNPROTECT_SHIP:
8212 case OP_SEND_MESSAGE:
8213 case OP_SELF_DESTRUCT:
8214 case OP_NEXT_MISSION:
8215 case OP_END_CAMPAIGN:
8216 case OP_END_OF_CAMPAIGN:
8217 case OP_SABOTAGE_SUBSYSTEM:
8218 case OP_REPAIR_SUBSYSTEM:
8219 case OP_INVALIDATE_GOAL:
8220 case OP_VALIDATE_GOAL:
8221 case OP_SEND_RANDOM_MESSAGE:
8222 case OP_TRANSFER_CARGO:
8223 case OP_EXCHANGE_CARGO:
8224 case OP_JETTISON_CARGO:
8225 case OP_CARGO_NO_DEPLETE:
8227 case OP_TRAINING_MSG:
8228 case OP_SET_TRAINING_CONTEXT_FLY_PATH:
8229 case OP_SET_TRAINING_CONTEXT_SPEED:
8230 case OP_END_MISSION_DELAY:
8231 case OP_SET_SUBSYSTEM_STRNGTH:
8232 case OP_GOOD_REARM_TIME:
8233 case OP_GRANT_PROMOTION:
8234 case OP_GRANT_MEDAL:
8236 case OP_ALLOW_WEAPON:
8237 case OP_TECH_ADD_SHIP:
8238 case OP_TECH_ADD_WEAPON:
8239 case OP_WARP_BROKEN:
8240 case OP_WARP_NOT_BROKEN:
8242 case OP_WARP_ALLOWED:
8243 case OP_FLASH_HUD_GAUGE:
8244 case OP_GOOD_SECONDARY_TIME:
8245 case OP_SHIP_VISIBLE:
8246 case OP_SHIP_INVISIBLE:
8247 case OP_SHIP_VULNERABLE:
8248 case OP_SHIP_INVULNERABLE:
8249 case OP_SHIP_GUARDIAN:
8250 case OP_SHIP_VANISH:
8251 case OP_SHIP_NO_GUARDIAN:
8253 case OP_MODIFY_VARIABLE:
8256 case OP_BEAM_FREE_ALL:
8258 case OP_BEAM_LOCK_ALL:
8259 case OP_TURRET_FREE:
8260 case OP_TURRET_FREE_ALL:
8261 case OP_TURRET_LOCK:
8262 case OP_TURRET_LOCK_ALL:
8263 case OP_ADD_REMOVE_ESCORT:
8264 case OP_AWACS_SET_RADIUS:
8265 case OP_SEND_MESSAGE_LIST:
8266 case OP_CAP_WAYPOINT_SPEED:
8267 case OP_TURRET_TAGGED_ONLY_ALL:
8268 case OP_TURRET_TAGGED_CLEAR_ALL:
8269 case OP_SUBSYS_SET_RANDOM:
8270 case OP_SUPERNOVA_START:
8271 case OP_SET_SPECIAL_WARPOUT_NAME:
8277 case OP_AI_WARP: // this particular operator is obsolete
8278 case OP_AI_WARP_OUT:
8279 case OP_AI_WAYPOINTS:
8280 case OP_AI_WAYPOINTS_ONCE:
8281 case OP_AI_DESTROY_SUBSYS:
8282 case OP_AI_CHASE_WING:
8283 case OP_AI_DISABLE_SHIP:
8284 case OP_AI_DISARM_SHIP:
8286 case OP_AI_GUARD_WING:
8287 case OP_AI_CHASE_ANY:
8288 case OP_AI_EVADE_SHIP:
8289 case OP_AI_STAY_NEAR_SHIP:
8290 case OP_AI_KEEP_SAFE_DISTANCE:
8292 case OP_AI_STAY_STILL:
8293 case OP_AI_PLAY_DEAD:
8303 // returns the data type of a specified argument to an operator. Argnum is 0 indexed.
8304 int query_operator_argument_type(int op, int argnum)
8310 SDL_assert(index >= 0 && index < Num_operators);
8311 op = Operators[index].value;
8314 for (index=0; index<Num_operators; index++)
8315 if (Operators[index].value == op)
8318 SDL_assert(index < Num_operators);
8321 if (argnum >= Operators[index].max)
8327 case OP_MISSION_TIME:
8330 case OP_WAYPOINT_MISSED:
8331 case OP_WAYPOINT_TWICE:
8333 case OP_GRANT_PROMOTION:
8334 case OP_WAS_PROMOTION_GRANTED:
8339 case OP_AND_IN_SEQUENCE:
8351 case OP_GREATER_THAN:
8353 case OP_HAS_TIME_ELAPSED:
8355 case OP_SET_TRAINING_CONTEXT_SPEED:
8356 case OP_END_MISSION_DELAY:
8357 case OP_SPECIAL_CHECK:
8358 case OP_AI_WARP_OUT:
8360 return OPF_POSITIVE;
8362 case OP_AI_WARP: // this operator is obsolete
8363 case OP_SET_TRAINING_CONTEXT_FLY_PATH:
8365 return OPF_WAYPOINT_PATH;
8369 case OP_AI_WAYPOINTS:
8370 case OP_AI_WAYPOINTS_ONCE:
8372 return OPF_WAYPOINT_PATH;
8374 return OPF_POSITIVE;
8376 case OP_IS_DISABLED:
8377 case OP_IS_DISARMED:
8378 case OP_TIME_SHIP_DESTROYED:
8379 case OP_TIME_SHIP_ARRIVED:
8380 case OP_TIME_SHIP_DEPARTED:
8381 case OP_SHIELDS_LEFT:
8383 case OP_CLEAR_SHIP_GOALS:
8384 case OP_PROTECT_SHIP:
8385 case OP_UNPROTECT_SHIP:
8386 case OP_BEAM_PROTECT_SHIP:
8387 case OP_BEAM_UNPROTECT_SHIP:
8388 case OP_TRANSFER_CARGO:
8389 case OP_EXCHANGE_CARGO:
8390 case OP_SHIP_INVISIBLE:
8391 case OP_SHIP_VISIBLE:
8392 case OP_SHIP_INVULNERABLE:
8393 case OP_SHIP_VULNERABLE:
8394 case OP_SHIP_GUARDIAN:
8395 case OP_SHIP_VANISH:
8396 case OP_SHIP_NO_GUARDIAN:
8397 case OP_SECONDARIES_DEPLETED:
8398 case OP_SPECIAL_WARP_DISTANCE:
8399 case OP_SET_SPECIAL_WARPOUT_NAME:
8400 case OP_IS_SHIP_VISIBLE:
8403 case OP_IS_DESTROYED:
8404 case OP_HAS_ARRIVED:
8405 case OP_HAS_DEPARTED:
8406 case OP_CLEAR_GOALS:
8407 return OPF_SHIP_WING;
8409 case OP_IS_DISABLED_DELAY:
8410 case OP_IS_DISARMED_DELAY:
8412 return OPF_POSITIVE;
8420 return OPF_POSITIVE;
8424 return OPF_WAYPOINT_PATH;
8426 return OPF_POSITIVE;
8431 return OPF_AI_ORDER;
8433 return OPF_SHIP_WING;
8435 case OP_IS_DESTROYED_DELAY:
8436 case OP_HAS_ARRIVED_DELAY:
8437 case OP_HAS_DEPARTED_DELAY:
8438 case OP_LAST_ORDER_TIME:
8440 return OPF_POSITIVE;
8442 return OPF_SHIP_WING;
8445 return OPF_SHIP_WING_POINT;
8447 case OP_MODIFY_VARIABLE:
8449 return OPF_VARIABLE_NAME;
8451 return OPF_AMBIGUOUS;
8455 case OP_HAS_UNDOCKED:
8456 case OP_HAS_DOCKED_DELAY:
8457 case OP_HAS_UNDOCKED_DELAY:
8458 case OP_TIME_DOCKED:
8459 case OP_TIME_UNDOCKED:
8463 return OPF_POSITIVE;
8465 case OP_TIME_WING_DESTROYED:
8466 case OP_TIME_WING_ARRIVED:
8467 case OP_TIME_WING_DEPARTED:
8468 case OP_CLEAR_WING_GOALS:
8471 case OP_IS_SUBSYSTEM_DESTROYED:
8472 case OP_HITS_LEFT_SUBSYSTEM:
8476 return OPF_SUBSYSTEM;
8481 else if (argnum == 1)
8482 return OPF_POSITIVE;
8484 return OPF_SUBSYSTEM;
8486 case OP_IS_SUBSYSTEM_DESTROYED_DELAY:
8489 else if ( argnum == 1 )
8490 return OPF_SUBSYSTEM;
8492 return OPF_POSITIVE;
8501 case OP_ADD_SHIP_GOAL:
8507 case OP_ADD_WING_GOAL:
8515 return OPF_SHIP_WING;
8526 case OP_AI_DISABLE_SHIP:
8527 case OP_AI_DISARM_SHIP:
8528 case OP_AI_EVADE_SHIP:
8529 case OP_AI_STAY_NEAR_SHIP:
8534 return OPF_POSITIVE;
8539 return OPF_SHIP_WING;
8541 return OPF_POSITIVE;
8544 case OP_AI_KEEP_SAFE_DISTANCE:
8545 return OPF_POSITIVE;
8550 else if (argnum == 1)
8551 return OPF_DOCKER_POINT;
8552 else if (argnum == 2)
8553 return OPF_DOCKEE_POINT;
8555 return OPF_POSITIVE;
8557 case OP_AI_CHASE_WING:
8558 case OP_AI_GUARD_WING:
8562 return OPF_POSITIVE;
8564 case OP_AI_DESTROY_SUBSYS:
8567 else if (argnum == 1)
8568 return OPF_SUBSYSTEM;
8570 return OPF_POSITIVE;
8575 case OP_SEND_MESSAGE:
8576 case OP_SEND_RANDOM_MESSAGE:
8578 return OPF_WHO_FROM;
8579 else if ( argnum == 1 )
8580 return OPF_PRIORITY;
8584 case OP_SEND_MESSAGE_LIST:
8587 // every four, the value repeats
8592 return OPF_WHO_FROM;
8594 return OPF_PRIORITY;
8598 return OPF_POSITIVE;
8600 case OP_TRAINING_MSG:
8604 return OPF_POSITIVE;
8606 case OP_SELF_DESTRUCT:
8609 case OP_NEXT_MISSION:
8610 return OPF_MISSION_NAME;
8612 case OP_END_CAMPAIGN:
8613 case OP_END_OF_CAMPAIGN:
8616 case OP_PREVIOUS_GOAL_TRUE:
8617 case OP_PREVIOUS_GOAL_FALSE:
8619 return OPF_MISSION_NAME;
8620 else if (argnum == 1 )
8621 return OPF_GOAL_NAME;
8625 case OP_PREVIOUS_GOAL_INCOMPLETE:
8626 return OPF_GOAL_NAME;
8628 case OP_PREVIOUS_EVENT_TRUE:
8629 case OP_PREVIOUS_EVENT_FALSE:
8630 case OP_PREVIOUS_EVENT_INCOMPLETE:
8632 return OPF_MISSION_NAME;
8633 else if ( argnum == 1 )
8634 return OPF_EVENT_NAME;
8638 case OP_SABOTAGE_SUBSYSTEM:
8639 case OP_REPAIR_SUBSYSTEM:
8640 case OP_SET_SUBSYSTEM_STRNGTH:
8642 return OPF_SHIP_NOT_PLAYER;
8643 else if (argnum == 1 )
8644 return OPF_SUBSYSTEM;
8646 return OPF_POSITIVE;
8648 case OP_WAYPOINTS_DONE:
8650 return OPF_SHIP_WING;
8652 return OPF_WAYPOINT_PATH;
8654 case OP_WAYPOINTS_DONE_DELAY:
8656 return OPF_SHIP_WING;
8657 else if ( argnum == 1 )
8658 return OPF_WAYPOINT_PATH;
8660 return OPF_POSITIVE;
8662 case OP_INVALIDATE_GOAL:
8663 case OP_VALIDATE_GOAL:
8664 return OPF_GOAL_NAME;
8666 case OP_SHIP_TYPE_DESTROYED:
8668 return OPF_POSITIVE;
8670 return OPF_SHIP_TYPE;
8672 case OP_KEY_PRESSED:
8674 return OPF_KEYPRESS;
8676 return OPF_POSITIVE;
8679 return OPF_KEYPRESS;
8682 case OP_EVENT_FALSE:
8683 return OPF_EVENT_NAME;
8685 case OP_EVENT_INCOMPLETE:
8686 case OP_EVENT_TRUE_DELAY:
8687 case OP_EVENT_FALSE_DELAY:
8689 return OPF_EVENT_NAME;
8691 return OPF_POSITIVE;
8693 case OP_GOAL_INCOMPLETE:
8694 case OP_GOAL_TRUE_DELAY:
8695 case OP_GOAL_FALSE_DELAY:
8697 return OPF_GOAL_NAME;
8699 return OPF_POSITIVE;
8701 case OP_AI_PLAY_DEAD:
8702 case OP_AI_CHASE_ANY:
8703 return OPF_POSITIVE;
8705 case OP_AI_STAY_STILL:
8707 return OPF_SHIP_POINT;
8709 return OPF_POSITIVE;
8711 case OP_GOOD_REARM_TIME:
8715 return OPF_POSITIVE;
8717 case OP_NUM_PLAYERS:
8718 return OPF_POSITIVE;
8720 case OP_SKILL_LEVEL_AT_LEAST:
8721 return OPF_SKILL_LEVEL;
8723 case OP_GRANT_MEDAL:
8724 case OP_WAS_MEDAL_GRANTED:
8725 return OPF_MEDAL_NAME;
8727 case OP_IS_CARGO_KNOWN:
8730 case OP_CARGO_KNOWN_DELAY:
8732 return OPF_POSITIVE;
8736 case OP_HAS_BEEN_TAGGED_DELAY:
8737 if ( argnum == 0 ) {
8738 return OPF_POSITIVE;
8743 case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
8744 if ( argnum == 0 ) {
8745 return OPF_POSITIVE;
8746 } else if ( argnum == 1 ) {
8749 return OPF_SUBSYSTEM;
8753 case OP_TECH_ADD_SHIP:
8754 return OPF_SHIP_CLASS_NAME;
8756 case OP_ALLOW_WEAPON:
8757 case OP_TECH_ADD_WEAPON:
8758 return OPF_WEAPON_NAME;
8760 case OP_WARP_BROKEN:
8761 case OP_WARP_NOT_BROKEN:
8763 case OP_WARP_ALLOWED:
8766 case OP_FLASH_HUD_GAUGE:
8767 return OPF_HUD_GAUGE_NAME;
8769 case OP_GOOD_SECONDARY_TIME:
8772 else if ( argnum == 1 )
8773 return OPF_POSITIVE;
8774 else if ( argnum == 2 )
8775 return OPF_HUGE_WEAPON;
8779 case OP_PERCENT_SHIPS_DEPARTED:
8780 case OP_PERCENT_SHIPS_DESTROYED:
8782 return OPF_POSITIVE;
8784 return OPF_SHIP_WING;
8788 case OP_DEPART_NODE_DELAY:
8790 return OPF_POSITIVE;
8791 } else if ( argnum == 1 ){
8792 return OPF_JUMP_NODE_NAME;
8797 case OP_DESTROYED_DEPARTED_DELAY:
8799 return OPF_POSITIVE;
8801 return OPF_SHIP_WING;
8804 case OP_JETTISON_CARGO:
8808 return OPF_POSITIVE;
8811 case OP_CARGO_NO_DEPLETE:
8823 return OPF_SUBSYSTEM;
8827 return OPF_SUBSYSTEM;
8836 case OP_NUM_TYPE_KILLS:
8840 return OPF_SHIP_TYPE;
8843 case OP_NUM_CLASS_KILLS:
8847 return OPF_SHIP_CLASS_NAME;
8852 case OP_TURRET_FREE:
8853 case OP_TURRET_LOCK:
8857 return OPF_SUBSYSTEM;
8860 case OP_BEAM_FREE_ALL:
8861 case OP_BEAM_LOCK_ALL:
8862 case OP_TURRET_FREE_ALL:
8863 case OP_TURRET_LOCK_ALL:
8864 case OP_TURRET_TAGGED_ONLY_ALL:
8865 case OP_TURRET_TAGGED_CLEAR_ALL:
8868 case OP_ADD_REMOVE_ESCORT:
8875 case OP_AWACS_SET_RADIUS:
8878 } else if(argnum == 1){
8879 return OPF_AWACS_SUBSYSTEM;
8884 case OP_CAP_WAYPOINT_SPEED:
8891 case OP_SUBSYS_SET_RANDOM:
8894 } else if (argnum == 1 || argnum == 2) {
8897 return OPF_SUBSYSTEM;
8900 case OP_SUPERNOVA_START:
8903 case OP_SHIELD_RECHARGE_PCT:
8904 case OP_WEAPON_RECHARGE_PCT:
8905 case OP_ENGINE_RECHARGE_PCT:
8908 case OP_SHIELD_QUAD_LOW:
8915 case OP_SECONDARY_AMMO_PCT:
8922 case OP_IS_SECONDARY_SELECTED:
8923 case OP_IS_PRIMARY_SELECTED:
8937 void update_block_names(const char *old_name, const char *new_name)
8941 for (i=0; i<MAX_SEXP_VARIABLES; i++) {
8942 if (Sexp_variables[i].type & SEXP_VARIABLE_BLOCK) {
8943 if ( !SDL_strcasecmp(old_name, Sexp_variables[i].variable_name) ) {
8944 SDL_strlcpy(Sexp_variables[i].variable_name, new_name, TOKEN_LENGTH);
8950 // DA: 1/7/99 Used to rename ships and waypoints, not variables
8951 // Strictly used in FRED
8952 void update_sexp_references(char *old_name, char *new_name)
8956 // update_block_names
8957 update_block_names(old_name, new_name);
8959 SDL_assert(strlen(new_name) < TOKEN_LENGTH);
8960 for (i=0; i<MAX_SEXP_NODES; i++){
8961 if ((SEXP_NODE_TYPE(i) == SEXP_ATOM) && (Sexp_nodes[i].subtype == SEXP_ATOM_STRING)){
8962 if (!SDL_strcasecmp(CTEXT(i), old_name)){
8963 SDL_strlcpy(CTEXT(i), new_name, TOKEN_LENGTH);
8969 // DA: 1/7/99 Used to rename event names, goal names, not variables
8970 // Strictly used in FRED
8971 void update_sexp_references(char *old_name, char *new_name, int format)
8975 SDL_assert(strlen(new_name) < TOKEN_LENGTH);
8976 for (i=0; i<MAX_SEXP_NODES; i++){
8977 if (is_sexp_top_level(i)){
8978 update_sexp_references(old_name, new_name, format, i);
8983 // DA: 1/7/99 Used to rename event names, goal names, not variables
8984 // recursive function to update references to a certain type of data
8985 void update_sexp_references(char *old_name, char *new_name, int format, int node)
8993 if ((SEXP_NODE_TYPE(node) == SEXP_LIST) && (Sexp_nodes[node].subtype == SEXP_ATOM_LIST)) {
8994 if (Sexp_nodes[node].first){
8995 update_sexp_references(old_name, new_name, format, Sexp_nodes[node].first);
8997 if (Sexp_nodes[node].rest){
8998 update_sexp_references(old_name, new_name, format, Sexp_nodes[node].rest);
9004 if (SEXP_NODE_TYPE(node) != SEXP_ATOM){
9008 if (Sexp_nodes[node].subtype != SEXP_ATOM_OPERATOR){
9012 op = identify_operator(CTEXT(node));
9013 SDL_assert(Sexp_nodes[node].first < 0);
9014 n = Sexp_nodes[node].rest;
9017 if (SEXP_NODE_TYPE(n) == SEXP_LIST){
9018 update_sexp_references(old_name, new_name, format, Sexp_nodes[n].first);
9020 SDL_assert((SEXP_NODE_TYPE(n) == SEXP_ATOM) && ((Sexp_nodes[n].subtype == SEXP_ATOM_NUMBER) || (Sexp_nodes[n].subtype == SEXP_ATOM_STRING)));
9021 if (query_operator_argument_type(op, i) == format) {
9022 if (!SDL_strcasecmp(CTEXT(n), old_name)){
9023 SDL_strlcpy(CTEXT(n), new_name, TOKEN_LENGTH);
9028 n = Sexp_nodes[n].rest;
9033 int query_referenced_in_sexp(int mode, char *name, int *node)
9037 for (n=0; n<MAX_SEXP_NODES; n++){
9038 if ((SEXP_NODE_TYPE(n) == SEXP_ATOM) && (Sexp_nodes[n].subtype == SEXP_ATOM_STRING)){
9039 if (!SDL_strcasecmp(CTEXT(n), name)){
9045 if (n == MAX_SEXP_NODES){
9053 // so we know it's being used somewhere.. Time to find out where..
9054 for (i=0; i<MAX_SHIPS; i++)
9055 if (Ships[i].objnum >= 0) {
9056 if (query_node_in_sexp(n, Ships[i].arrival_cue)){
9057 return i | SRC_SHIP_ARRIVAL;
9059 if (query_node_in_sexp(n, Ships[i].departure_cue)){
9060 return i | SRC_SHIP_DEPARTURE;
9064 for (i=0; i<MAX_WINGS; i++){
9065 if (Wings[i].wave_count) {
9066 if (query_node_in_sexp(n, Wings[i].arrival_cue)){
9067 return i | SRC_WING_ARRIVAL;
9069 if (query_node_in_sexp(n, Wings[i].departure_cue)){
9070 return i | SRC_WING_DEPARTURE;
9075 for (i=0; i<Num_mission_events; i++){
9076 if (query_node_in_sexp(n, Mission_events[i].formula)){
9077 return i | SRC_EVENT;
9081 for (i=0; i<Num_goals; i++){
9082 if (query_node_in_sexp(n, Mission_goals[i].formula)){
9083 return i | SRC_MISSION_GOAL;
9087 for (j=0; j<Num_teams; j++) {
9088 for (i=0; i<Debriefings[j].num_stages; i++) {
9089 if (query_node_in_sexp(n, Debriefings[j].stages[i].formula)){
9090 return i | SRC_DEBRIEFING;
9095 for (j=0; j<Num_teams; j++) {
9096 for (i=0; i<Briefings[j].num_stages; i++) {
9097 if (query_node_in_sexp(n, Briefings[j].stages[i].formula)){
9098 return i | SRC_BRIEFING;
9106 int verify_vector(char *text)
9111 for (i=0; i<Num_waypoint_lists; i++) {
9112 len = strlen(str = Waypoint_lists[i].name);
9113 if (!SDL_strncasecmp(str, text, len)){
9114 if (!text[len] || text[len] == ':'){
9120 if (i < Num_waypoint_lists) {
9122 return 0; // a valid waypoint path
9125 str = &text[len + 1];
9127 if (!isdigit(*str++)){
9128 return -1; // not a valid number
9132 z = atoi(&text[len + 1]);
9133 if (z < 1 || z > Waypoint_lists[i].count){
9134 return -1; // out of range
9137 return 0; // a valid waypoint
9141 if (text[0] != '(' || text[len - 1] != ')'){
9153 if (validate_float(&str)){
9158 if (validate_float(&str)){
9163 if (validate_float(&str)){
9181 void skip_white(char **str)
9183 if ((**str == ' ') || (**str == '\t')){
9188 int validate_float(char **str)
9190 int count = 0, dot = 0;
9192 while (isdigit(**str) || **str == '.') {
9212 // check if operator return type opr is a valid match for operator argument type opf
9213 int sexp_query_type_match(int opf, int opr)
9217 return ((opr == OPR_NUMBER) || (opr == OPR_POSITIVE));
9220 return (opr == OPR_POSITIVE);
9223 return (opr == OPR_BOOL);
9226 return (opr == OPR_NULL);
9229 return (opr == OPR_AI_GOAL);
9235 const char *sexp_error_message(int num)
9238 case SEXP_CHECK_NONOP_ARGS:
9239 return "Data shouldn't have arguments";
9241 case SEXP_CHECK_OP_EXPTECTED:
9242 return "Operator expected instead of data";
9244 case SEXP_CHECK_UNKNOWN_OP:
9245 return "Unrecognized operator";
9247 case SEXP_CHECK_TYPE_MISMATCH:
9248 return "Argument type mismatch";
9250 case SEXP_CHECK_BAD_ARG_COUNT:
9251 return "Argument count is illegal";
9253 case SEXP_CHECK_UNKNOWN_TYPE:
9254 return "Unknown operator argument type";
9256 case SEXP_CHECK_INVALID_NUM:
9257 return "Not a number";
9259 case SEXP_CHECK_INVALID_SHIP:
9260 return "Invalid ship name";
9262 case SEXP_CHECK_INVALID_WING:
9263 return "Invalid wing name";
9265 case SEXP_CHECK_INVALID_SUBSYS:
9266 return "Invalid subsystem name";
9268 case SEXP_CHECK_INVALID_IFF:
9269 return "Invalid team name";
9271 case SEXP_CHECK_INVALID_POINT:
9272 return "Invalid point";
9274 case SEXP_CHECK_NEGATIVE_NUM:
9275 return "Negative number not allowed";
9277 case SEXP_CHECK_INVALID_SHIP_WING:
9278 return "Invalid ship/wing name";
9280 case SEXP_CHECK_INVALID_SHIP_TYPE:
9281 return "Invalid ship type";
9283 case SEXP_CHECK_UNKNOWN_MESSAGE:
9284 return "Invalid message name";
9286 case SEXP_CHECK_INVALID_PRIORITY:
9287 return "Invalid priority";
9289 case SEXP_CHECK_INVALID_MISSION_NAME:
9290 return "Invalid mission filename";
9292 case SEXP_CHECK_INVALID_GOAL_NAME:
9293 return "Invalid goal name";
9295 case SEXP_CHECK_INVALID_LEVEL:
9296 return "Mission level too low in tree";
9298 case SEXP_CHECK_INVALID_MSG_SOURCE:
9299 return "Invalid message source";
9301 case SEXP_CHECK_INVALID_DOCKER_POINT:
9302 return "Invalid docker point";
9304 case SEXP_CHECK_INVALID_DOCKEE_POINT:
9305 return "Invalid dockee point";
9307 case SEXP_CHECK_ORDER_NOT_ALLOWED:
9308 return "Ship not allowed to have this order";
9310 case SEXP_CHECK_DOCKING_NOT_ALLOWED:
9311 return "Ship can't dock with target ship";
9313 case SEXP_CHECK_NUM_RANGE_INVALID:
9314 return "Number is out of range";
9316 case SEXP_CHECK_INVALID_EVENT_NAME:
9317 return "Event name is invalid (not known)";
9319 case SEXP_CHECK_INVALID_SKILL_LEVEL:
9320 return "Skill level name is invalid (not known)";
9322 case SEXP_CHECK_INVALID_MEDAL_NAME:
9323 return "Invalid medal name";
9325 case SEXP_CHECK_INVALID_WEAPON_NAME:
9326 return "Invalid weapon name";
9328 case SEXP_CHECK_INVALID_SHIP_CLASS_NAME:
9329 return "Invalid ship class name";
9331 case SEXP_CHECK_INVALID_GAUGE_NAME:
9332 return "Invalid HUD gauges name";
9334 case SEXP_CHECK_INVALID_JUMP_NODE:
9335 return "Invalid Jump Node name";
9338 SDL_snprintf(Sexp_error_text, SDL_arraysize(Sexp_error_text), "Sexp error code %d", num);
9339 return Sexp_error_text;
9342 int query_sexp_ai_goal_valid(int sexp_ai_goal, int ship)
9346 for (op=0; op<Num_operators; op++)
9347 if (Operators[op].value == sexp_ai_goal)
9350 SDL_assert(op < Num_operators);
9351 for (i=0; i<Num_sexp_ai_goal_links; i++)
9352 if (Sexp_ai_goal_links[i].op_code == sexp_ai_goal)
9355 SDL_assert(i < Num_sexp_ai_goal_links);
9356 return ai_query_goal_valid(ship, Sexp_ai_goal_links[i].ai_goal);
9359 // Takes an Sexp_node.text pointing to a variable (of form "Sexp_variables[xx]=string" or "Sexp_variables[xx]=number")
9360 // and returns the index into the Sexp_variables array of the actual value
9361 int extract_sexp_variable_index(int node)
9363 char *text = Sexp_nodes[node].text;
9369 start_index = text + 15;
9370 SDL_assert(isdigit(*start_index));
9374 while ( *start_index != ']' ) {
9375 char_index[len++] = *(start_index++);
9376 SDL_assert(len < 3);
9379 SDL_assert(len > 0);
9380 char_index[len] = 0; // append null termination to string
9382 variable_index = atoi(char_index);
9383 SDL_assert( (variable_index >= 0) && (variable_index < MAX_SEXP_VARIABLES) );
9385 return variable_index;
9389 // wrapper around Sexp_node[xx].text for normal and variable
9392 if (Sexp_nodes[n].type & SEXP_FLAG_VARIABLE) {
9393 int sexp_variable_index;
9395 sexp_variable_index = get_index_sexp_variable_name(Sexp_nodes[n].text);
9396 SDL_assert(sexp_variable_index != -1);
9398 // sexp_variable_index = extract_sexp_variable_index(n);
9399 sexp_variable_index = atoi(Sexp_nodes[n].text);
9401 // Reference a Sexp_variable
9402 // string format -- "Sexp_variables[xx]=number" or "Sexp_variables[xx]=string", where xx is the index
9404 SDL_assert( !(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_NOT_USED) );
9405 SDL_assert(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SET);
9407 return Sexp_variables[sexp_variable_index].text;
9409 return Sexp_nodes[n].text;
9414 // Set all Sexp_variables to type uninitialized
9415 void init_sexp_vars()
9417 for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
9418 // if ( !(Sexp_nodes[i].type & SEXP_FLAG_PERSISTENT) )
9419 // Sexp_nodes[i].type = SEXP_NOT_USED;
9420 Sexp_variables[i].type = SEXP_VARIABLE_NOT_USED;
9425 // Adds an Sexp_variable to be used in a mission.
9426 // This should be called from within mission parse.
9427 int sexp_add_variable(const char *text, const char *var_name, int type, int index)
9429 // if index == -1, find next open slot
9431 for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
9432 if (Sexp_variables[i].type == SEXP_VARIABLE_NOT_USED) {
9438 SDL_assert( (index >= 0) && (index < MAX_SEXP_VARIABLES) );
9442 SDL_strlcpy(Sexp_variables[index].text, text, TOKEN_LENGTH);
9443 SDL_strlcpy(Sexp_variables[index].variable_name, var_name, TOKEN_LENGTH);
9444 Sexp_variables[index].type &= ~SEXP_VARIABLE_NOT_USED;
9445 Sexp_variables[index].type = (type | SEXP_VARIABLE_SET);
9452 // Modifies and Sexp_variable to be used in a mission
9453 // This should be called in mission when an sexp_variable is to be modified
9454 void sexp_modify_variable(char *text, int index)
9456 SDL_assert(index >= 0 && index < MAX_SEXP_VARIABLES);
9457 SDL_assert(Sexp_variables[index].type & SEXP_VARIABLE_SET);
9458 SDL_assert( !MULTIPLAYER_CLIENT );
9460 SDL_strlcpy(Sexp_variables[index].text, text, TOKEN_LENGTH);
9461 Sexp_variables[index].type |= SEXP_VARIABLE_MODIFIED;
9463 // do multi_callback_here
9466 void sexp_modify_variable(int n)
9468 int sexp_variable_index;
9470 // char *new_char_var;
9471 char number_as_str[TOKEN_LENGTH];
9473 // Only do single player of multi host
9474 if ( MULTIPLAYER_CLIENT ) {
9480 // get sexp_variable index
9481 SDL_assert(Sexp_nodes[n].first == -1);
9482 sexp_variable_index = atoi(Sexp_nodes[n].text);
9484 // verify variable set
9485 SDL_assert(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SET);
9487 if (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_NUMBER) {
9488 // get new numerical value
9489 new_number = eval_sexp(Sexp_nodes[n].rest);
9491 SDL_snprintf(number_as_str, SDL_arraysize(number_as_str), "%d", new_number);
9492 sexp_modify_variable(number_as_str, sexp_variable_index);
9495 SDL_assert(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING);
9497 char *new_text = Sexp_nodes[Sexp_nodes[n].rest].text;
9498 sexp_modify_variable(new_text, sexp_variable_index);
9505 // Different type needed for Fred (1) allow modification of type (2) no callback required
9506 void sexp_fred_modify_variable(const char *text, const char *var_name, int index, int type)
9508 SDL_assert(index >= 0 && index < MAX_SEXP_VARIABLES);
9509 SDL_assert(Sexp_variables[index].type & SEXP_VARIABLE_SET);
9510 SDL_assert( (type & SEXP_VARIABLE_NUMBER) || (type & SEXP_VARIABLE_STRING) );
9512 SDL_strlcpy(Sexp_variables[index].text, text, TOKEN_LENGTH);
9513 SDL_strlcpy(Sexp_variables[index].variable_name, var_name, TOKEN_LENGTH);
9514 Sexp_variables[index].type = (SEXP_VARIABLE_SET | SEXP_VARIABLE_MODIFIED | type);
9517 // return index of sexp_variable_name, -1 if not found
9518 int get_index_sexp_variable_name(const char* temp_name)
9520 for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
9521 if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
9522 // check case sensitive
9523 if ( !strcmp(Sexp_variables[i].variable_name, temp_name) ) {
9534 // counts number of sexp_variables that are set
9535 int sexp_variable_count()
9539 for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
9540 if ( (Sexp_variables[i].type & SEXP_VARIABLE_SET) && !(Sexp_variables[i].type & SEXP_VARIABLE_BLOCK) ) {
9549 // deletes sexp_variable from active
9550 void sexp_variable_delete(int index)
9552 SDL_assert(Sexp_variables[index].type & SEXP_VARIABLE_SET);
9554 Sexp_variables[index].type = SEXP_VARIABLE_NOT_USED;
9557 int sexp_var_compare(const void *var1, const void *var2)
9560 sexp_variable *sexp_var1, *sexp_var2;
9562 sexp_var1 = (sexp_variable*) var1;
9563 sexp_var2 = (sexp_variable*) var2;
9565 set1 = sexp_var1->type & SEXP_VARIABLE_SET;
9566 set2 = sexp_var2->type & SEXP_VARIABLE_SET;
9568 if (!set1 && !set2) {
9570 } else if (set1 && !set2) {
9572 } else if (!set1 && set2) {
9575 return SDL_strcasecmp( sexp_var1->variable_name, sexp_var2->variable_name);
9579 // Count number of variables in block
9580 int sexp_variable_block_count()
9583 for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
9584 if (Sexp_variables[i].type & SEXP_VARIABLE_BLOCK) {
9592 // Sort sexp_variable list lexigraphically, with set before unset
9593 void sexp_variable_sort()
9595 qsort( (void *)Sexp_variables, (size_t)(MAX_SEXP_VARIABLES - sexp_variable_block_count()), sizeof(sexp_variable), sexp_var_compare );
9598 int sexp_variable_allocate_block(const char* block_name, int block_type)
9600 int num_blocks, block_count, var_count, start;
9601 block_count = sexp_variable_block_count();
9602 var_count = sexp_variable_count();
9604 if (block_type & SEXP_VARIABLE_BLOCK_EXP) {
9605 num_blocks = BLOCK_EXP_SIZE;
9607 Int3(); // add new block type here with size
9611 if (block_count + var_count > (MAX_SEXP_VARIABLES - num_blocks)) {
9612 // not enough free space
9616 // squeeze all variables to front of array
9617 sexp_variable_sort();
9619 // squeeze all block to end of array
9620 sexp_variable_condense_block();
9622 start = MAX_SEXP_VARIABLES - block_count - num_blocks;
9624 for (int idx=start; idx<start+num_blocks; idx++) {
9625 SDL_assert(Sexp_variables[idx].type == SEXP_VARIABLE_NOT_USED);
9626 Sexp_variables[idx].type = SEXP_VARIABLE_BLOCK | block_type;
9627 SDL_strlcpy(Sexp_variables[idx].variable_name, block_name, TOKEN_LENGTH);
9633 // squeeze all blocks to top of array
9634 void sexp_variable_condense_block()
9636 int temp_idx, idx, var_count;
9638 var_count = sexp_variable_count();
9639 temp_idx = MAX_SEXP_VARIABLES-1;
9641 for (idx=MAX_SEXP_VARIABLES-1; idx>var_count-1; idx--) {
9642 if (Sexp_variables[idx].type & SEXP_VARIABLE_BLOCK) {
9643 if (temp_idx > idx) {
9644 Sexp_variables[temp_idx] = Sexp_variables[idx];
9645 Sexp_variables[idx].type = SEXP_VARIABLE_NOT_USED;
9653 void sexp_variable_block_free(const char *ship_name, int start_index, int block_type)
9657 if (block_type & SEXP_VARIABLE_BLOCK_EXP) {
9658 num_blocks = BLOCK_EXP_SIZE;
9660 Int3(); // new type of block
9664 for (int i=start_index; i<(start_index + num_blocks); i++) {
9665 SDL_assert(!SDL_strcasecmp(Sexp_variables[i].variable_name, ship_name));
9667 SDL_assert(Sexp_variables[i].type & block_type);
9669 Sexp_variables[i].type = SEXP_VARIABLE_NOT_USED;
9672 sexp_variable_condense_block();
9675 // evaluate number which may result from an operator or may be text
9676 // only useful for first operand
9677 int num_eval(int node)
9679 if ( CAR(node) != -1 ) {
9680 return eval_sexp(CAR(node));
9682 return atoi(CTEXT(node));