/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ /* * $Logfile: /Freespace2/code/Fred2/Sexp_tree.cpp $ * $Revision: 1.18 $ * $Date: 2007-07-13 22:28:11 $ * $Author: turey $ * * Sexp tree handler class. Almost everything is handled by this class. * * $Log: not supported by cvs2svn $ * Revision 1.17 2007/02/20 04:20:10 Goober5000 * the great big duplicate model removal commit * * Revision 1.16 2007/01/15 13:42:59 karajorma * Hmmm. Forgot to commit changes to support network variables and setting ammo/weapons to HEAD as well as 3.6.9. * * Revision 1.15 2006/12/28 00:59:20 wmcoolmon * WMC codebase commit. See pre-commit build thread for details on changes. * * Revision 1.14 2006/10/09 05:25:18 Goober5000 * make sexp nodes dynamic * * Revision 1.13 2006/10/09 04:44:58 Goober5000 * preliminary commit of sexp node bump... fred only, just changing some names for clarity * * Revision 1.12 2006/09/13 17:29:43 taylor * fix for Mantis bug #1006 * * Revision 1.11 2006/08/06 19:24:56 Goober5000 * deprecate change-ship-model * * Revision 1.10 2006/07/31 21:13:03 Goober5000 * fix for Mantis #1020 * --Goober5000 * * Revision 1.9 2006/06/02 09:46:03 karajorma * Prevented the modify variable dialog deleting or changing the type of variables which are used in Team Loadout * * Revision 1.8 2006/03/01 04:01:37 Goober5000 * fix comm message localization * * Revision 1.7 2006/02/26 01:32:23 Goober5000 * bah * * Revision 1.6 2006/02/26 00:43:09 Goober5000 * fix subsystems for get-object-* * * Revision 1.5 2006/02/04 07:05:03 Goober5000 * fixed several IFF bugs in FRED (plus one or two other bugs) * --Goober5000 * * Revision 1.4 2006/02/02 07:00:29 Goober5000 * consolidated comm order stuff * --Goober5000 * * Revision 1.3 2006/02/02 06:22:58 Goober5000 * replaced Fred_comm_orders with proper Comm_orders, just for WMC ;) * --Goober5000 * * Revision 1.2 2006/01/30 06:27:59 taylor * dynamic starfield bitmaps * * Revision 1.1 2006/01/19 02:27:31 Goober5000 * import FRED2 back into fs2_open module * --Goober5000 * * Revision 1.114 2006/01/16 00:34:34 Goober5000 * final (I hope) IFF fixage involving the FRED dialogs :p * --Goober5000 * * Revision 1.113 2006/01/15 01:01:34 Goober5000 * forgot one small thing * --Goober5000 * * Revision 1.112 2006/01/14 23:49:01 Goober5000 * second pass; all the errors are fixed now; one more thing to take care of * --Goober5000 * * Revision 1.111 2006/01/14 05:24:18 Goober5000 * first pass at converting FRED to use new IFF stuff * --Goober5000 * * Revision 1.110 2006/01/08 02:56:02 wmcoolmon * Oops,. FRED2 stuff for target order * * Revision 1.109 2005/12/29 08:21:00 wmcoolmon * No my widdle FRED, I didn't forget about you ^_^ (codebase commit) * * Revision 1.108 2005/11/01 03:38:39 Goober5000 * allow variables in modes other than event mode (like briefings and debriefings) * --Goober5000 * * Revision 1.107 2005/10/29 22:55:51 Goober5000 * hmm, looks like this never hit CVS * --Goober5000 * * Revision 1.106 2005/10/28 05:47:39 phreak * Default values for add-sun-bitmap and add-background-bitmap. * * Revision 1.105 2005/10/28 05:26:54 phreak * some more changes to get the variable arguments correctly working for add-bg/sun-bitmap * * Revision 1.104 2005/10/24 02:54:15 phreak * Get the new nebula argument types working in fred. * * Revision 1.103 2005/10/23 04:47:08 phreak * adjust the variable autofill hack since i need to add the scale information for add-sun-bitmap and add-background-bitmap * * Revision 1.102 2005/10/23 04:20:04 phreak * Have "add-background-bitmap" and "add-sun-bitmap" autofill their variable arguments. * * Revision 1.101 2005/08/23 07:28:02 Goober5000 * grammar * --Goober5000 * * Revision 1.100 2005/06/29 18:57:58 taylor * add support for set-skybox-model sexp * don't divide Num_sexp_help again since it should be right from sexp.cpp (was making only the first 30 or so sexps have help) * * Revision 1.99 2005/05/25 07:26:14 Goober5000 * fixed karajorma's last FRED bug as well as an inadvertent bug I introduced last commit * --Goober5000 * * Revision 1.98 2005/05/25 06:04:17 Goober5000 * removed some duplicate code and fixed a bug * --Goober5000 * * Revision 1.97 2005/05/25 05:09:28 Goober5000 * fixed a bug that caused some sexps' arguments to revert to their defaults * --Goober5000 * * Revision 1.96 2005/05/25 03:58:29 Goober5000 * fixed a bug where manually entering an invalid sexp caused a crash * --Goober5000 * * Revision 1.95 2005/05/18 06:00:01 phreak * updates since the Sexp_help structure went into sexp.cpp in the main project. * * Revision 1.94 2005/04/18 03:15:45 Goober5000 * made error more friendly * --Goober5000 * * Revision 1.93 2005/03/29 03:43:11 phreak * ai directory fixes as well as fixes for the new jump node code * * Revision 1.92 2005/03/27 13:01:21 Goober5000 * stuff for FRED * --Goober5000 * * Revision 1.91 2005/03/15 07:35:13 Goober5000 * sexp help for the new sexps * --Goober5000 * * Revision 1.90 2005/03/06 22:43:14 wmcoolmon * Fixx0red some of the jump node errors * * Revision 1.89 2005/01/29 05:40:27 Goober5000 * regular docking should work again in FRED now (multiple docking still isn't done yet) * --Goober5000 * * Revision 1.88 2004/12/23 16:03:01 phreak * sexp help for scramble-messages and unscramble-messages * -phreak * * Revision 1.87 2004/12/15 19:16:13 Goober5000 * FRED code for custom wing names * --Goober5000 * * Revision 1.86 2004/11/17 22:24:13 Goober5000 * the FRED part * --Goober5000 * * Revision 1.85 2004/10/15 10:05:19 Goober5000 * fred help for change-alt-name * --Goober5000 * * Revision 1.84 2004/10/15 09:26:17 Goober5000 * small sexp help update * --Goober5000 * * Revision 1.83 2004/10/14 01:20:04 Goober5000 * more ubersexp bugfixing * --Goober5000 * * Revision 1.82 2004/09/22 21:54:30 Goober5000 * two small fixes * --Goober5000 * * Revision 1.81 2004/09/22 21:52:23 Goober5000 * further work on ubersexp framework * --Goober5000 * * Revision 1.80 2004/09/22 08:32:06 Goober5000 * fixed some small problems in the framework - should be working nicely now; * next step is the actual sexp implementation :-P * --Goober5000 * * Revision 1.79 2004/09/22 06:57:34 Goober5000 * FRED framework for when-argument * --Goober5000 * * Revision 1.78 2004/09/17 08:07:53 Goober5000 * a bit of reordering * --Goober5000 * * Revision 1.77 2004/09/17 07:14:05 Goober5000 * fixed FRED stuff for warp effect, etc. * --Goober5000 * * Revision 1.76 2004/09/17 00:28:32 Goober5000 * removed player-not-use-ai and changed player-use-ai to take an argument * --Goober5000 * * Revision 1.75 2004/09/17 00:18:52 Goober5000 * sexp help for new hud stuff * --Goober5000 * * Revision 1.74 2004/08/23 04:33:41 Goober5000 * FRED support for ship-tag * --Goober5000 * * Revision 1.73 2004/07/29 01:37:26 Kazan * Autopilot SEXPs update * * Revision 1.72 2004/06/15 20:50:28 wmcoolmon * hud-set-color, current-speed, and FRED fixes (Wouldn't compile). * * Revision 1.71 2004/06/09 00:19:48 wmcoolmon * hud-set-color SEXP description * * Revision 1.70 2004/06/01 07:38:39 wmcoolmon * New HUD SEXP stuff. * * Revision 1.69 2004/05/11 02:16:35 Goober5000 * accidentally committed this in fs2_open, so I should do it here too * --Goober5000 * * Revision 1.68 2004/03/15 12:14:46 randomtiger * Fixed a whole heap of problems with Fred introduced by changes to global vars. * * Revision 1.67 2004/01/14 06:28:39 Goober5000 * made set-support-ship number align with general FS convention * --Goober5000 * * Revision 1.66 2003/11/11 21:29:58 Goober5000 * forgot the FRED one :-P * --Goober5000 * * Revision 1.65 2003/10/20 11:49:17 Goober5000 * added min, max, and avg sexps * --Goober5000 * * Revision 1.64 2003/09/30 04:06:43 Goober5000 * removed orphaned sexp help left over from _argv[-1]'s stuff * --Goober5000 * * Revision 1.63 2003/09/11 23:21:53 Goober5000 * Rolled back _argv[-1]'s changes to the sexp infrastructure, which would * have broken a huge amount of stuff and created some nasty bugs. * Also commented most of _argv[-1]'s new sexps pending consolidation, * and added a placeholder help entry for the one good sexp. * --Goober5000 * * Revision 1.62 2003/09/05 10:04:33 Goober5000 * persistent variable checkboxes in FRED * --Goober5000 * * Revision 1.61 2003/09/05 07:43:56 Goober5000 * fix some compile errors * --Goober5000 * * Revision 1.60 2003/09/05 05:06:32 Goober5000 * merged num-ships-in-battle and num-ships-in-battle-team, making the * team argument optional * --Goober5000 * * Revision 1.59 2003/09/05 04:53:50 Goober5000 * merge of Phreak's and my Sexp_tree.cpp * --Goober5000 * * Revision 1.58 2003/09/05 02:16:18 phreak * added an option to loop sounds played by play-sound-from-file, so i updated the sexp help * * Revision 1.57 2003/08/30 22:00:49 phreak * added sexp help for num-ships-in-battle and num-ships-in-battle-team * * Revision 1.56 2003/08/28 06:29:17 Goober5000 * ah, 'tworks :) * now fixing up the FRED part * basically the last few commits were bugfixes * --Goober5000 * * Revision 1.55 2003/08/27 02:04:54 Goober5000 * added percent-ships-disarmed and percent-ships-disabled * --Goober5000 * * Revision 1.54 2003/08/27 01:38:00 Goober5000 * added is-ship-type, is-ship-class, lock-rotating-subsystem, and free-rotating-subsystem; * also fixed the argument and return values for various sexps so that they work * properly for negative numbers * --Goober5000 * * Revision 1.53 2003/08/21 09:00:20 Goober5000 * fixed set-support-ship sexp * --Goober5000 * * Revision 1.52 2003/06/19 18:13:27 phreak * added help for turret-tagged-specific and turret-tagged-clear-specific * * Revision 1.51 2003/05/24 16:48:49 phreak * added help info for Sesquipedalian's kamikaze and not-kamikaze sexps * * Revision 1.50 2003/04/12 19:09:16 Goober5000 * fixed an omission that caused a subcategory not to show up * --Goober5000 * * Revision 1.49 2003/04/05 20:47:58 Goober5000 * gotta love those compiler errors ;) * fixed those and cleaned up conflicts with the missile-locked sexp * --Goober5000 * * Revision 1.48 2003/03/30 21:01:19 Goober5000 * Ugh. Fixed a dumb bug where FRED wouldn't check the right subsystem for * some sexps. * --Goober5000 * * Revision 1.47 2003/03/29 11:24:25 sesquipedalian * Aaaand nevermind... * * Revision 1.46 2003/03/29 08:51:54 sesquipedalian * Added is-missile-locked sexp * * Revision 1.45 2003/03/22 07:24:53 Goober5000 * bleah * --Goober5000 * * Revision 1.44 2003/03/21 08:43:23 Goober5000 * d'oh * --Goober5000 * * Revision 1.43 2003/03/21 08:33:34 Goober5000 * yuck - fixed a nasty error when FRED provides a default value for warp-effect * and explosion-effect * --Goober5000 * * Revision 1.42 2003/03/21 04:51:33 Goober5000 * added get-relative-object-*, where * = x, y, and z; these sexps return the * world coordinates of a set of relative coordinates to an object; also, fixed many * places in sexp.cpp so that now sexps can accept other sexps as parameters, * wherease before they weren't able to * --Goober5000 * * Revision 1.41 2003/03/20 09:17:16 Goober5000 * implemented EMP as part of weapon-effect sexp * --Goober5000 * * Revision 1.40 2003/03/20 04:27:10 Goober5000 * extended sexps * --Goober5000 * * Revision 1.39 2003/03/20 00:28:37 Goober5000 * he doesn't make sexps, he makes them better * --Goober5000 * even though I do make sexps :) * * Revision 1.38 2003/03/20 00:08:09 Goober5000 * making sexps better * --Goober5000 * * Revision 1.37 2003/03/19 06:27:26 Goober5000 * sexp help * --Goober5000 * * Revision 1.36 2003/03/18 08:45:33 Goober5000 * added explosion-effect sexp and did some other minor housekeeping * --Goober5000 * * Revision 1.35 2003/03/03 04:15:24 Goober5000 * FRED portion of tech room fix, plus some neatening up of the dialogs * --Goober5000 * * Revision 1.34 2003/01/26 20:02:06 Goober5000 * clarified help for damaged-escort-list and damaged-escort-list-all * --Goober5000 * * Revision 1.33 2003/01/26 18:37:19 Goober5000 * changed change-music to change-soundtrack * --Goober5000 * * Revision 1.32 2003/01/25 04:17:39 Goober5000 * added change-music sexp and bumped MAX_SOUNDTRACKS from 10 to 25 * --Goober5000 * * Revision 1.31 2003/01/21 17:24:16 Goober5000 * fixed a few bugs in Bobboau's implementation of the glow sexps; also added * help for the sexps in sexp_tree * --Goober5000 * * Revision 1.30 2003/01/19 22:20:22 Goober5000 * fixed a bunch of bugs -- the support ship sexp, the "no-subspace-drive" flag, * and departure into hangars should now all work properly * --Goober5000 * * Revision 1.29 2003/01/19 07:45:38 Goober5000 * actually added the set-support-ship sexp; much of the other commit was * groundwork (data types and stuff) * --Goober5000 * * Revision 1.28 2003/01/19 07:02:15 Goober5000 * fixed a bunch of bugs - "no-subspace-drive" should now work properly for * all ships, and all ships who have their departure anchor set to a capital ship * should exit to that ship when told to depart * --Goober5000 * * Revision 1.27 2003/01/10 04:14:18 Goober5000 * I found these two beautiful functions in ship.cpp - ship_change_model * and change_ship_type - so I made them into sexps :) * --Goober5000 * * Revision 1.26 2003/01/07 20:06:44 Goober5000 * added ai-chase-any-except sexp * --Goober5000 * * Revision 1.25 2003/01/05 01:26:35 Goober5000 * added capability of is-iff and change-iff to have wings as well as ships * as their arguments; also allowed a bunch of sexps to accept the player * as an argument where they would previously display a parse error * --Goober5000 * * Revision 1.24 2003/01/04 23:15:39 Goober5000 * fixed the order sexp * --Goober5000 * * Revision 1.23 2003/01/03 21:58:07 Goober5000 * Fixed some minor bugs, and added a primitive-sensors flag, where if a ship * has primitive sensors it can't target anything and objects don't appear * on radar if they're outside a certain range. This range can be modified * via the sexp primitive-sensors-set-range. * --Goober5000 * * Revision 1.22 2003/01/02 00:35:20 Goober5000 * added don't-collide-invisible and collide-invisible sexps * --Goober5000 * * Revision 1.21 2003/01/01 23:33:33 Goober5000 * added ship-vaporize and ship-no-vaporize sexps * --Goober5000 * * Revision 1.20 2002/12/31 08:20:31 Goober5000 * simplified end-user implementation of ballistic primaries * --Goober5000 * * Revision 1.19 2002/12/27 20:00:11 phreak * added damage-escort-list to sexp tree help * * Revision 1.18 2002/12/27 03:14:47 Goober5000 * removed the existing stealth sexps and replaced them with the following: * ship-stealthy * ship-unstealthy * is-ship-stealthy * friendly-stealth-invisible * friendly-stealth-visible * is-friendly-stealth-visible * --Goober5000 * * Revision 1.17 2002/12/25 01:22:23 Goober5000 * meh - changed is-cargo-x to is-cargo * --Goober5000 * * Revision 1.16 2002/12/24 07:42:29 Goober5000 * added change-ai-class and is-ai-class, and I think I may also have nailed the * is-iff bug; did some other bug hunting as well * --Goober5000 * * Revision 1.15 2002/12/23 23:01:27 Goober5000 * added set-cargo and is-cargo-x sexps * --Goober5000 * * Revision 1.14 2002/12/23 05:18:52 Goober5000 * Squashed some Volition bugs! :O Some of the sexps for dealing with more than * one ship would return after only dealing with the first ship. * * Also added the following sexps: * is-ship-stealthed * ship-force-stealth * ship-force-nostealth * ship-remove-stealth-forcing * * They toggle the stealth flag on and off. If a ship is forced stealthy, it won't even * show up for friendly ships. * --Goober5000 * * Revision 1.13 2002/12/22 21:15:27 Goober5000 * added primaries-depleted and primary-ammo-pct sexps -- useful for ships with * ballistic primaries * --Goober5000 * * Revision 1.12 2002/12/22 17:23:38 Goober5000 * Subcategories implemented. :) So far all that's been done is the Change menu, but other * subcategorizations are possible. Here are the instructions from sexp.h... * "Adding more subcategories is possible with the new code. All that needs to be done is * to add a #define here (a number from 0x0000 to 0x00ff ORred with the category that it * goes under), some appropriate case statements in get_subcategory() (in sexp.cpp) that * will return the subcategory for each sexp that uses it, and the submenu name in the * op_submenu[] array in sexp_tree.cpp." * * Please note that I rearranged a whole bunch of sexps in the Operators[] array in sexp.cpp * in order to make the subcategories work better, so if you get a whole bunch of differences * or even conflicts, just ignore them. :) * --Goober5000 * * Revision 1.11 2002/12/21 17:59:32 Goober5000 * rearranged the sexp list and got the preliminary subcategories working - still need to work on the actual submenu * --Goober5000 * * Revision 1.10 2002/12/17 03:19:22 Goober5000 * help for set-scanned and set-unscanned * --Goober5000 * * Revision 1.9 2002/12/13 21:10:01 Goober5000 * --fixed an annoying minor bug with distance-ship-subsystem where the sexp tree displayed subsystems for the wrong ship * --allowed the "hidden sexps" grant-medal, grant-promotion, was-medal-granted, was-promotion-granted, tech-add-ships, and tech-add-weapons to be selectable * --cleaned up grammar in the help structure a bit * ~Goober5000~ * * Revision 1.8 2002/12/12 19:16:51 Goober5000 * corrected /t to be \t * * Revision 1.7 2002/12/12 07:57:58 Goober5000 * added help for distance-ship-subsystem sexp; fixed up some grammar and spelling * ~Goober5000~ * * Revision 1.6 2002/12/10 04:49:00 righteous1 * Update: Port CATAGORY spelling update to FRED2_OPEN. * Committer: R1 * * Revision 1.5 2002/11/28 00:25:57 sesquipedalian * fixed end-mission help text error * * Revision 1.4 2002/11/28 00:01:40 sesquipedalian * end-mission sexp added * * Revision 1.3 2002/10/29 22:42:11 sesquipedalian * no message * * Revision 1.2 2002/08/15 01:06:34 penguin * Include filename reorg (to coordinate w/ fs2_open) * * Revision 1.1.1.1 2002/07/15 03:11:02 inquisitor * Initial FRED2 Checking * * * 43 9/07/99 9:22p Jefff * added 2 more assignable medals * * 42 9/07/99 1:05a Andsager * Added team-score sexp for multi team vs team missions * * 41 8/27/99 4:07p Andsager * Add is-ship-visible sexp. Make ship-vanish sexp SINGLE player only * * 40 8/24/99 4:25p Andsager * Add ship-vanish sexp * * 39 8/16/99 10:04p Andsager * Add special-warp-dist and special-warpout-name sexp for Knossos device * warpout. * * 38 8/09/99 2:00p Dave * 2 new sexpressions. * * 37 8/02/99 4:26p Dave * Added 2 new sexpressions. * * 36 8/02/99 1:43p Andsager * format fix * * 35 7/28/99 1:36p Andsager * Modify cargo1 to include flag CARGO_NO_DEPLETE. Add sexp * cargo-no-deplete (only for BIG / HUGE). Modify ship struct to pack * better. * * 34 7/24/99 4:56p Dave * Added 3 new sexpressions. * * 33 7/21/99 8:10p Dave * First run of supernova effect. * * 32 7/20/99 9:19p Andsager * Added facing waypoint sexp * * 31 7/20/99 9:54a Andsager * Add subsys-set-random sexp * * 30 7/19/99 12:02p Andsager * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to * only blow up subsystem if its strength is > 0 * * 29 7/13/99 3:37p Andsager * Add secondaries-depleted sexp * * 28 7/12/99 12:01p Andsager * Make message by default come from command. * * 27 7/08/99 12:06p Andsager * Add turret-tagged-only and turret-tagged-clear sexp. * * 26 6/29/99 10:08a Andsager * Add guardian sexp * * 25 6/23/99 5:51p Andsager * Add waypoint-cap-speed. Checkin stealth ai - inactive. * * 24 6/16/99 10:21a Dave * Added send-message-list sexpression. * * 23 6/01/99 8:35p Dave * Finished lockarm weapons. Added proper supercap weapons/damage. Added * awacs-set-radius sexpression. * * 22 5/24/99 11:28a Dave * Sexpression for adding/removing ships from the hud escort list. * * 21 5/20/99 1:40p Andsager * Fix find_text() to only look at nodes that are used. * * 20 5/04/99 5:21p Andsager * * 19 4/28/99 9:33a Andsager * Add turret-free and turret-lock (and -all) sexp. Stargger start time * of beam weapons beam-free and beam-free-all. * * 18 4/26/99 2:14p Andsager * Add beam-protect-ship and beam-unprotect-ship sexp. * * 17 4/23/99 12:01p Johnson * Added SIF_HUGE_SHIP * * 16 4/02/99 9:54a Dave * Added a few more options in the weapons.tbl for beam weapons. Attempt * at putting "pain" packets into multiplayer. * * 15 3/20/99 3:46p Dave * Added support for model-based background nebulae. Added 3 new * sexpressions. * * 14 3/04/99 6:09p Dave * Added in sexpressions for firing beams and checking for if a ship is * tagged. * * 13 3/01/99 10:00a Dave * Fxied several dogfight related stats bugs. * * 12 2/26/99 6:01p Andsager * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay * * 11 1/26/99 10:09a Andsager * Better checking for modifying/deleting variables * * 10 1/25/99 5:16p Andsager * Handle change of variable type on modify-variable * * 9 1/25/99 8:10a Andsager * Add sexp_modify_variable(). Changed syntax checking to allow, adding * operator return type ambiguous * * 8 1/24/99 11:37p Dave * First full rev of beam weapons. Very customizable. Removed some bogus * Int3()'s in low level net code. * * 7 1/20/99 9:02a Andsager * Fix bug in verify_and_fix_arguments, where list of strings can have * NULL strings. * * 6 1/19/99 3:57p Andsager * Round 2 of variables * * 5 12/17/98 2:39p Andsager * Added bitmaps for campaign editor. Changed input into insert() to * include bitmaps * * 4 12/17/98 2:34p Andsager * new bitmap and dialog for campaign editor * * 3 10/13/98 9:27a Dave * Started neatening up freespace.h * * 2 10/07/98 6:28p Dave * Initial checkin. Renamed all relevant stuff to be Fred2 instead of * Fred. Globalized mission and campaign file extensions. Removed Silent * Threat specific code. * * 1 10/07/98 3:02p Dave * * 1 10/07/98 3:00p Dave * * 179 9/25/98 1:33p Andsager * Add color to event editor (root and chain) indicating mission directive * * 178 9/15/98 4:26p Allender * added sexpression help for some sexpressions * * 177 6/09/98 5:15p Lawrance * French/German localization * * 176 5/21/98 12:58a Hoffoss * Fixed warnings optimized build turned up. * * 175 5/15/98 6:45p Hoffoss * Made some things not appear in the release version of Fred. * * 174 5/14/98 10:15a Allender * add optional argument to prevous-goal/event operators to specify what * sexpression should return when being played as a single mission * * 173 5/04/98 10:57a Johnson * Fixed bug with labeled roots allowing insert. * * 172 4/25/98 7:39p Allender * fixd some small hotkey stuff. Worked on turret orientation being * correct for multiplayer. new sexpression called end-campaign will will * end the main campaign * * 171 4/23/98 5:49p Hoffoss * Added tracking of techroom database list info in pilot files, added * sexp to add more to list, made mouse usable on ship listing in tech * room. * * 170 4/15/98 3:46p Hoffoss * Fixed bug with getting a default argument value from an opf listing was * utilizing temporary memory that was being destroyed before we were * finished with it. * * 169 4/14/98 5:46p Hoffoss * Added special-check operator. * * 168 4/14/98 5:24p Hoffoss * Added a custom operator for training handling for Mike K. * * 167 4/14/98 4:19p Jim * Fixed bug with deleting an argument to an operator that you shouldn't * be allowed to. * * 166 4/07/98 10:51a Allender * remove any allied from message senders. Make heads for mission * specific messages play appropriately * * 165 4/03/98 12:17a Allender * new sexpression to detect departed or destroyed. optionally disallow * support ships. Allow docking with escape pods * * 164 3/30/98 2:57p Hoffoss * Fixed event listing in campaign editor mode. * * 163 3/26/98 3:13p Duncan * Fixed bug in goal name listing generation function. Allender forgot * about an assumption being made with them when he used it for * invalidate-goal. * * 162 3/23/98 2:46p Hoffoss * Fixed bug with default argument available for OPF_MESSAGE even when * there were no messages, and added "#Command" as a message source to * listing. * * 161 3/21/98 7:36p Lawrance * Move jump nodes to own lib. * * * $NoKeywords: $ */ #include "stdafx.h" #include "Sexp_tree.h" #include "FRED.h" #include "FREDDoc.h" #include "Management.h" #include "parse/sexp.h" #include "OperatorArgTypeSelect.h" #include "globalincs/linklist.h" #include "EventEditor.h" #include "MissionGoalsDlg.h" #include "ai/aigoals.h" #include "mission/missionmessage.h" #include "mission/missioncampaign.h" #include "CampaignEditorDlg.h" #include "hud/hudsquadmsg.h" #include "IgnoreOrdersDlg.h" #include "stats/medals.h" #include "controlconfig/controlsconfig.h" #include "hud/hudgauges.h" #include "starfield/starfield.h" #include "nebula/neb.h" #include "nebula/neblightning.h" #include "jumpnode/jumpnode.h" #include "AddVariableDlg.h" #include "ModifyVariableDlg.h" #include "gamesnd/eventmusic.h" // for change-soundtrack #include "menuui/techmenu.h" // for intel stuff #include "weapon/emp.h" #include "gamesnd/gamesnd.h" #include "weapon/weapon.h" #include "hud/hudartillery.h" #include "iff_defs/iff_defs.h" #define TREE_NODE_INCREMENT 100 #define MAX_OP_MENUS 30 #define MAX_SUBMENUS (MAX_OP_MENUS * MAX_OP_MENUS) #define ID_VARIABLE_MENU 0xda00 #define ID_ADD_MENU 0xdc00 #define ID_REPLACE_MENU 0xde00 // note: stay below 0xe000 so we don't collide with MFC defines.. #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif BEGIN_MESSAGE_MAP(sexp_tree, CTreeCtrl) //{{AFX_MSG_MAP(sexp_tree) ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag) ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() ON_WM_DESTROY() ON_WM_LBUTTONDOWN() ON_NOTIFY_REFLECT(TVN_KEYDOWN, OnKeydown) //}}AFX_MSG_MAP END_MESSAGE_MAP() static int Add_count, Replace_count; static int Modify_variable; extern sexp_help_struct Sexp_help[]; extern op_menu_struct op_menu[]; extern op_menu_struct op_submenu[]; // constructor sexp_tree::sexp_tree() { select_sexp_node = -1; root_item = -1; m_mode = 0; m_dragging = FALSE; m_p_image_list = NULL; help_box = NULL; clear_tree(); } // clears out the tree, so all the nodes are unused. void sexp_tree::clear_tree(char *op) { mprintf(("Resetting dynamic tree node limit from %d to %d...\n", tree_nodes.size(), 0)); total_nodes = flag = 0; tree_nodes.clear(); if (op) { DeleteAllItems(); if (strlen(op)) { set_node(allocate_node(-1), (SEXPT_OPERATOR | SEXPT_VALID), op); build_tree(); } } } void sexp_tree::reset_handles() { uint i; for (i=0; i -1); sprintf(combined_name, "%s(%s)", Sexp_variables[sexp_var_index].variable_name, Sexp_variables[sexp_var_index].text); } // creates a tree from a given Sexp_nodes[] point under a given parent. Recursive. // Returns the allocated current node. int sexp_tree::load_branch(int index, int parent) { int cur = -1; char combined_var_name[2*TOKEN_LENGTH + 2]; while (index != -1) { Assert(Sexp_nodes[index].type != SEXP_NOT_USED); if (Sexp_nodes[index].subtype == SEXP_ATOM_LIST) { load_branch(Sexp_nodes[index].first, parent); // do the sublist and continue } else if (Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR) { cur = allocate_node(parent); if ((index == select_sexp_node) && !flag) { // translate sexp node to our node select_sexp_node = cur; flag = 1; } set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), Sexp_nodes[index].text); load_branch(Sexp_nodes[index].rest, cur); // operator is new parent now return cur; // 'rest' was just used, so nothing left to use. } else if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) { cur = allocate_node(parent); if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) { get_combined_variable_name(combined_var_name, Sexp_nodes[index].text); set_node(cur, (SEXPT_VARIABLE | SEXPT_NUMBER | SEXPT_VALID), combined_var_name); } else { set_node(cur, (SEXPT_NUMBER | SEXPT_VALID), Sexp_nodes[index].text); } } else if (Sexp_nodes[index].subtype == SEXP_ATOM_STRING) { cur = allocate_node(parent); if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) { get_combined_variable_name(combined_var_name, Sexp_nodes[index].text); set_node(cur, (SEXPT_VARIABLE | SEXPT_STRING | SEXPT_VALID), combined_var_name); } else { set_node(cur, (SEXPT_STRING | SEXPT_VALID), Sexp_nodes[index].text); } } else Assert(0); // unknown and/or invalid sexp type if ((index == select_sexp_node) && !flag) { // translate sexp node to our node select_sexp_node = cur; flag = 1; } index = Sexp_nodes[index].rest; if (index == -1) return cur; } return cur; } int sexp_tree::query_false(int node) { if (node < 0) node = root_item; Assert(node >= 0); Assert(tree_nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID)); Assert(tree_nodes[node].next == -1); // must make this assumption or else it will confuse code! if (get_operator_const(tree_nodes[node].text) == OP_FALSE){ return TRUE; } return FALSE; } // builds an sexp of the tree and returns the index of it. This allocates sexp nodes. int sexp_tree::save_tree(int node) { if (node < 0) node = root_item; Assert(node >= 0); Assert(tree_nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID)); Assert(tree_nodes[node].next == -1); // must make this assumption or else it will confuse code! return save_branch(node); } // get variable name from sexp_tree node .text void var_name_from_sexp_tree_text(char *var_name, const char *text) { int var_name_length = strcspn(text, "("); Assert(var_name_length < TOKEN_LENGTH - 1); strncpy(var_name, text, var_name_length); var_name[var_name_length] = '\0'; } #define NO_PREVIOUS_NODE -9 // called recursively to save a tree branch and everything under it int sexp_tree::save_branch(int cur, int at_root) { int start, node = -1, last = NO_PREVIOUS_NODE; char var_name_text[TOKEN_LENGTH]; start = -1; while (cur != -1) { if (tree_nodes[cur].type & SEXPT_OPERATOR) { node = alloc_sexp(tree_nodes[cur].text, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, save_branch(tree_nodes[cur].child)); if ((tree_nodes[cur].parent >= 0) && !at_root) { node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1); } } else if (tree_nodes[cur].type & SEXPT_NUMBER) { // allocate number, maybe variable if (tree_nodes[cur].type & SEXPT_VARIABLE) { var_name_from_sexp_tree_text(var_name_text, tree_nodes[cur].text); node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1); } else { node = alloc_sexp(tree_nodes[cur].text, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1); } } else if (tree_nodes[cur].type & SEXPT_STRING) { // allocate string, maybe variable if (tree_nodes[cur].type & SEXPT_VARIABLE) { var_name_from_sexp_tree_text(var_name_text, tree_nodes[cur].text); node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1); } else { node = alloc_sexp(tree_nodes[cur].text, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1); } } else if (tree_nodes[cur].type & SEXPT_STRING) { Assert( !(tree_nodes[cur].type & SEXPT_VARIABLE) ); Int3(); } else { Assert(0); // unknown and/or invalid type } if (last == NO_PREVIOUS_NODE){ start = node; } else if (last >= 0){ Sexp_nodes[last].rest = node; } last = node; Assert(last != NO_PREVIOUS_NODE); // should be impossible cur = tree_nodes[cur].next; if (at_root){ return start; } } return start; } // find the next free tree node and return its index. int sexp_tree::find_free_node() { int i; for (i = 0; i < (int)tree_nodes.size(); i++) { if (tree_nodes[i].type == SEXPT_UNUSED) return i; } return -1; } // allocate a node. Remains used until freed. int sexp_tree::allocate_node() { int node = find_free_node(); // need more tree nodes? if (node < 0) { int old_size = tree_nodes.size(); Assert(TREE_NODE_INCREMENT > 0); // allocate in blocks of TREE_NODE_INCREMENT tree_nodes.resize(tree_nodes.size() + TREE_NODE_INCREMENT); mprintf(("Bumping dynamic tree node limit from %d to %d...\n", old_size, tree_nodes.size())); #ifndef NDEBUG for (uint i = old_size; i < tree_nodes.size(); i++) { sexp_tree_item *item = &tree_nodes[i]; Assert(item->type == SEXPT_UNUSED); } #endif // our new sexp is the first out of the ones we just created node = old_size; } // reset the new node tree_nodes[node].type = SEXPT_UNINIT; tree_nodes[node].parent = -1; tree_nodes[node].child = -1; tree_nodes[node].next = -1; tree_nodes[node].flags = 0; strcpy(tree_nodes[node].text, ""); tree_nodes[node].handle = NULL; total_nodes++; return node; } // allocate a child node under 'parent'. Appends to end of list. int sexp_tree::allocate_node(int parent, int after) { int i, index = allocate_node(); if (parent != -1) { i = tree_nodes[parent].child; if (i == -1) { tree_nodes[parent].child = index; } else { while ((i != after) && (tree_nodes[i].next != -1)) i = tree_nodes[i].next; tree_nodes[index].next = tree_nodes[i].next; tree_nodes[i].next = index; } } tree_nodes[index].parent = parent; return index; } // free a node and all its children. Also clears pointers to it, if any. // node = node chain to free // cascade = 0: free just this node and children under it. (default) // !0: free this node and all siblings after it. // void sexp_tree::free_node(int node, int cascade) { int i; // clear the pointer to node i = tree_nodes[node].parent; Assert(i != -1); if (tree_nodes[i].child == node) tree_nodes[i].child = tree_nodes[node].next; else { i = tree_nodes[i].child; while (tree_nodes[i].next != -1) { if (tree_nodes[i].next == node) { tree_nodes[i].next = tree_nodes[node].next; break; } i = tree_nodes[i].next; } } if (!cascade) tree_nodes[node].next = -1; // now free up the node and its children free_node2(node); } // more simple node freer, which works recursively. It frees the given node and all siblings // that come after it, as well as all children of these. Doesn't clear any links to any of // these freed nodes, so make sure all links are broken first. (i.e. use free_node() if you can) // void sexp_tree::free_node2(int node) { Assert(node != -1); Assert(tree_nodes[node].type != SEXPT_UNUSED); Assert(total_nodes > 0); *modified = 1; tree_nodes[node].type = SEXPT_UNUSED; total_nodes--; if (tree_nodes[node].child != -1) free_node2(tree_nodes[node].child); if (tree_nodes[node].next != -1) free_node2(tree_nodes[node].next); } // initialize the data for a node. Should be called right after a new node is allocated. void sexp_tree::set_node(int node, int type, char *text) { Assert(type != SEXPT_UNUSED); Assert(tree_nodes[node].type != SEXPT_UNUSED); tree_nodes[node].type = type; size_t max_length; if (type & SEXPT_VARIABLE) { max_length = 2 * TOKEN_LENGTH + 2; } else { max_length = TOKEN_LENGTH; } Assert(strlen(text) < max_length); strcpy(tree_nodes[node].text, text); } void sexp_tree::post_load() { if (!flag) select_sexp_node = -1; } // build or rebuild a CTreeCtrl object with the current tree data void sexp_tree::build_tree() { if (!flag) select_sexp_node = -1; DeleteAllItems(); add_sub_tree(0, TVI_ROOT); } // Create the CTreeCtrl tree from the tree data. The tree data should already be setup by // this point. void sexp_tree::add_sub_tree(int node, HTREEITEM root) { // char str[80]; int node2; Assert(node >= 0 && node < (int)tree_nodes.size()); node2 = tree_nodes[node].child; // check for single argument operator case (prints as one line) /* if (node2 != -1 && tree_nodes[node2].child == -1 && tree_nodes[node2].next == -1) { sprintf(str, "%s %s", tree_nodes[node].text, tree_nodes[node2].text); tree_nodes[node].handle = insert(str, root); tree_nodes[node].flags = OPERAND | EDITABLE; tree_nodes[node2].flags = COMBINED; return; }*/ // bitmap to draw in tree int bitmap; if (tree_nodes[node].type & SEXPT_OPERATOR) { tree_nodes[node].flags = OPERAND; bitmap = BITMAP_OPERATOR; } else { if (tree_nodes[node].type & SEXPT_VARIABLE) { tree_nodes[node].flags = NOT_EDITABLE; bitmap = BITMAP_VARIABLE; } else { tree_nodes[node].flags = EDITABLE; bitmap = BITMAP_DATA; } } root = tree_nodes[node].handle = insert(tree_nodes[node].text, bitmap, bitmap, root); node = node2; while (node != -1) { Assert(node >= 0 && node < (int)tree_nodes.size()); Assert(tree_nodes[node].type & SEXPT_VALID); if (tree_nodes[node].type & SEXPT_OPERATOR) { add_sub_tree(node, root); } else { Assert(tree_nodes[node].child == -1); if (tree_nodes[node].type & SEXPT_VARIABLE) { tree_nodes[node].handle = insert(tree_nodes[node].text, BITMAP_VARIABLE, BITMAP_VARIABLE, root); tree_nodes[node].flags = NOT_EDITABLE; } else { tree_nodes[node].handle = insert(tree_nodes[node].text, BITMAP_DATA, BITMAP_DATA, root); tree_nodes[node].flags = EDITABLE; } } node = tree_nodes[node].next; } } // construct tree nodes for an sexp, adding them to the list and returning first node int sexp_tree::load_sub_tree(int index, bool valid, char *text) { int cur; if (index < 0) { cur = allocate_node(-1); set_node(cur, (SEXPT_OPERATOR | (valid ? SEXPT_VALID : 0)), text); // setup a default tree if none return cur; } // assumption: first token is an operator. I require this because it would cause problems // with child/parent relations otherwise, and it should be this way anyway, since the // return type of the whole sexp is boolean, and only operators can satisfy this. Assert(Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR); cur = load_branch(index, -1); return cur; } void sexp_tree::setup_selected(HTREEITEM h) { uint i; item_index = -1; item_handle = h; if (!h) item_handle = GetSelectedItem(); for (i=0; iGetMenuItemCount(); while (i--) { if ( (mptr = popup_menu->GetSubMenu(i)) > 0 ) { popup_menu->GetMenuString(i, buf, sizeof(buf), MF_BYPOSITION); if (!stricmp(buf, "add operator")) { add_op_menu = mptr; } else if (!stricmp(buf, "replace operator")) { replace_op_menu = mptr; } else if (!stricmp(buf, "add data")) { add_data_menu = mptr; } else if (!stricmp(buf, "replace data")) { replace_data_menu = mptr; } else if (!stricmp(buf, "insert operator")) { insert_op_menu = mptr; } else if (!stricmp(buf, "replace variable")) { replace_variable_menu = mptr; } } } // add popup menus for all the operator categories for (i=0; iAppendMenu(MF_POPUP, (UINT) add_op_submenu[i].m_hMenu, op_menu[i].name); replace_op_menu->AppendMenu(MF_POPUP, (UINT) replace_op_submenu[i].m_hMenu, op_menu[i].name); insert_op_menu->AppendMenu(MF_POPUP, (UINT) insert_op_submenu[i].m_hMenu, op_menu[i].name); } // get rid of the placeholders we needed to ensure popup menus stayed popup menus, // i.e. MSDEV will convert empty popup menus into normal menu items. add_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND); replace_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND); insert_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND); replace_variable_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND); // get item_index item_index = -1; for (i=0; i<(int)tree_nodes.size(); i++) { if (tree_nodes[i].handle == h) { item_index = i; break; } } /* Goober5000 - allow variables in all modes; the restriction seems unnecessary IMHO // Do SEXP_VARIABLE stuff here. if (m_mode != MODE_EVENTS) { // only allow variables in event mode menu.EnableMenuItem(ID_SEXP_TREE_ADD_VARIABLE, MF_GRAYED); menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED); } else */ { menu.EnableMenuItem(ID_SEXP_TREE_ADD_VARIABLE, MF_ENABLED); menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_ENABLED); // check not root (-1) if (item_index >= 0) { // get type of sexp_tree item clicked on int type = get_type(h); int parent = tree_nodes[item_index].parent; if (parent >= 0) { op = get_operator_index(tree_nodes[parent].text); Assert(op >= 0); int first_arg = tree_nodes[parent].child; // get arg count of item to replace Replace_count = 0; int temp = first_arg; while (temp != item_index) { Replace_count++; temp = tree_nodes[temp].next; // DB - added 3/4/99 if(temp == -1){ break; } } int op_type = query_operator_argument_type(op, Replace_count); // check argument type at this position // special case dont allow replace data for variable names // Goober5000 - why? the only place this happens is when replacing the ambiguous argument in // modify-variable with a variable, which seems legal enough. //if (op_type != OPF_AMBIGUOUS) { // Goober5000 - given the above, we have to figure out what type this stands for if (op_type == OPF_AMBIGUOUS) { type = tree_nodes[first_arg].type; } if ( (type & SEXPT_STRING) || (type & SEXPT_NUMBER) ) { int max_sexp_vars = MAX_SEXP_VARIABLES; // prevent collisions in id numbers: ID_VARIABLE_MENU + 512 = ID_ADD_MENU Assert(max_sexp_vars < 512); for (int idx=0; idxAppendMenu(flag, (ID_VARIABLE_MENU + idx), buf); } } } //} } } // cant modify if no variables if (sexp_variable_count() == 0) { menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED); } } // add all the submenu items first for (i=0; iTrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this); return; } Assert(item_index != -1); // handle not found, which should be impossible. if (!(tree_nodes[item_index].flags & EDITABLE)) { menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED); } if (tree_nodes[item_index].parent == -1) { // root node menu.EnableMenuItem(ID_DELETE, MF_GRAYED); // can't delete the root item. } /* if ((tree_nodes[item_index].flags & OPERAND) && (tree_nodes[item_index].flags & EDITABLE)) // expandable? menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED); z = tree_nodes[item_index].child; if (z != -1 && tree_nodes[z].next == -1 && tree_nodes[z].child == -1) menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED); z = tree_nodes[tree_nodes[item_index].parent].child; if (z != -1 && tree_nodes[z].next == -1 && tree_nodes[z].child == -1) menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);*/ // change enabled status of 'add' type menu options. add_type = 0; if (tree_nodes[item_index].flags & OPERAND) { add_type = OPR_STRING; int child = tree_nodes[item_index].child; Add_count = count_args(child); op = get_operator_index(tree_nodes[item_index].text); Assert(op >= 0); // get listing of valid argument values and add to menus type = query_operator_argument_type(op, Add_count); list = get_listing_opf(type, item_index, Add_count); if (list) { sexp_list_item *ptr; int data_idx = 0; ptr = list; while (ptr) { if (ptr->op >= 0) { // enable operators with correct return type menu.EnableMenuItem(Operators[ptr->op].value, MF_ENABLED); } else { // add data if ( (data_idx + 3) % 30) { add_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text); } else { add_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text); } } data_idx++; ptr = ptr->next; } } // special handling for the non-string formats if (type == OPF_NONE) { // an argument can't be added add_type = 0; } else if (type == OPF_NULL) { // arguments with no return values add_type = OPR_NULL; // Goober5000 } else if (type == OPF_FLEXIBLE_ARGUMENT) { add_type = OPR_FLEXIBLE_ARGUMENT; } else if (type == OPF_NUMBER) { // takes numbers add_type = OPR_NUMBER; menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED); } else if (type == OPF_POSITIVE) { // takes non-negative numbers add_type = OPR_POSITIVE; menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED); } else if (type == OPF_BOOL) { // takes true/false bool values add_type = OPR_BOOL; } else if (type == OPF_AI_GOAL) { add_type = OPR_AI_GOAL; } // add_type unchanged from above if (add_type == OPR_STRING) { menu.EnableMenuItem(ID_ADD_STRING, MF_ENABLED); } list->destroy(); } // disable operators that do not have arguments available for (j=0; j= 0) { replace_type = OPR_STRING; op = get_operator_index(tree_nodes[parent].text); Assert(op >= 0); int first_arg = tree_nodes[parent].child; count = count_args(tree_nodes[parent].child); // already at minimum number of arguments? if (count <= Operators[op].min) { menu.EnableMenuItem(ID_DELETE, MF_GRAYED); } // get arg count of item to replace Replace_count = 0; int temp = first_arg; while (temp != item_index) { Replace_count++; temp = tree_nodes[temp].next; // DB - added 3/4/99 if(temp == -1){ break; } } // maybe gray delete for (i=Replace_count+1; iop >= 0) { menu.EnableMenuItem(Operators[ptr->op].value | OP_REPLACE_FLAG, MF_ENABLED); } else { if ( (data_idx + 3) % 30) replace_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text); else replace_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text); } data_idx++; ptr = ptr->next; } } if (type == OPF_NONE) { // takes no arguments replace_type = 0; } else if (type == OPF_NUMBER) { // takes numbers replace_type = OPR_NUMBER; menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED); } else if (type == OPF_POSITIVE) { // takes non-negative numbers replace_type = OPR_POSITIVE; menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED); } else if (type == OPF_BOOL) { // takes true/false bool values replace_type = OPR_BOOL; } else if (type == OPF_NULL) { // takes operator that doesn't return a value replace_type = OPR_NULL; // Goober5000 } else if (type == OPF_FLEXIBLE_ARGUMENT) { replace_type = OPR_FLEXIBLE_ARGUMENT; } else if (type == OPF_AI_GOAL) { replace_type = OPR_AI_GOAL; } // default to string if (replace_type == OPR_STRING) { menu.EnableMenuItem(ID_REPLACE_STRING, MF_ENABLED); } // modify string or number if (modify_variable) if ( !stricmp(Operators[op].text, "modify-variable") ) { int modify_type = get_modify_variable_type(parent); if (modify_type == OPF_NUMBER) { menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED); menu.EnableMenuItem(ID_REPLACE_STRING, MF_GRAYED); } // no change for string type } list->destroy(); } else { // top node, so should be a Boolean type. if (m_mode == MODE_EVENTS) { // return type should be null replace_type = OPR_NULL; for (j=0; j= -1); if (z != -1) { op = get_operator_index(tree_nodes[z].text); Assert(op != -1); j = tree_nodes[z].child; count = 0; while (j != item_index) { count++; j = tree_nodes[j].next; } type = query_operator_argument_type(op, count); // check argument type at this position } else { if (m_mode == MODE_EVENTS) type = OPF_NULL; else type = OPF_BOOL; } for (j=0; j -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)) { Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST); if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) { j = get_operator_const(CTEXT(Sexp_clipboard)); Assert(j); z = query_operator_return_type(j); if ((z == OPR_POSITIVE) && (replace_type == OPR_NUMBER)) z = OPR_NUMBER; // Goober5000's number hack if ((z == OPR_NUMBER) && (replace_type == OPR_POSITIVE)) z = OPR_POSITIVE; if (replace_type == z) menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED); z = query_operator_return_type(j); if ((z == OPR_POSITIVE) && (add_type == OPR_NUMBER)) z = OPR_NUMBER; if (add_type == z) menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED); } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) { if ((replace_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1)) menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED); else if (replace_type == OPR_NUMBER) menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED); if ((add_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1)) menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED); else if (add_type == OPR_NUMBER) menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED); } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) { if (replace_type == OPR_STRING) menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED); if (add_type == OPR_STRING) menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED); } else Int3(); // unknown and/or invalid sexp type } if (!(menu.GetMenuState(ID_DELETE, MF_BYCOMMAND) & MF_GRAYED)) menu.EnableMenuItem(ID_EDIT_CUT, MF_ENABLED); gray_menu_tree(popup_menu); popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this); } } // counts the number of arguments an operator has. Call this with the node of the first // argument of the operator int sexp_tree::count_args(int node) { int count = 0; while (node != -1) { count++; node = tree_nodes[node].next; } return count; } // identify what type of argument this is. You call it with the node of the first argument // of an operator. It will search through enough of the arguments to determine what type of // data they are. int sexp_tree::identify_arg_type(int node) { int type = -1; while (node != -1) { Assert(tree_nodes[node].type & SEXPT_VALID); switch (SEXPT_TYPE(tree_nodes[node].type)) { case SEXPT_OPERATOR: type = get_operator_const(tree_nodes[node].text); Assert(type); return query_operator_return_type(type); case SEXPT_NUMBER: return OPR_NUMBER; case SEXPT_STRING: // either a ship or a wing type = SEXP_ATOM_STRING; break; // don't return, because maybe we can narrow selection down more. } node = tree_nodes[node].next; } return type; } // determine if an item should be editable. This doesn't actually edit the label. int sexp_tree::edit_label(HTREEITEM h) { uint i; for (i=0; ihandler(ROOT_RENAMED, item_index, str); return 1; } else Int3(); // root labels shouldn't have been editable! } Assert(node < tree_nodes.size()); if (tree_nodes[node].type & SEXPT_OPERATOR) { str = match_closest_operator(str, node); if (!str) return 0; // Goober5000 - avoids crashing SetItemText(h, str); item_index = node; add_or_replace_operator(get_operator_index(str), 1); r = 0; } // Error checking would not hurt here len = strlen(str); if (len >= TOKEN_LENGTH) len = TOKEN_LENGTH - 1; strncpy(tree_nodes[node].text, str, len); tree_nodes[node].text[len] = 0; /* node = tree_nodes[node].parent; if (node != -1) { child = tree_nodes[node].child; if (child != -1 && tree_nodes[child].next == -1 && tree_nodes[child].child == -1) { merge_operator(child); return 0; } }*/ return r; } // Look for the valid operator that is the closest match for 'str' and return the operator // number of it. What operators are valid is determined by 'node', and an operator is valid // if it is allowed to fit at position 'node' // char *sexp_tree::match_closest_operator(char *str, int node) { int z, i, op, arg_num, opf, opr; char *sub_best = NULL, *best = NULL; z = tree_nodes[node].parent; if (z < 0) { return str; } op = get_operator_index(tree_nodes[z].text); if (op < 0) return str; // determine which argument we are of the parent arg_num = find_argument_number(z, tree_nodes[z].child); opf = query_operator_argument_type(op, arg_num); // check argument type at this position opr = query_operator_return_type(op); for (i=0; i 0) ) sub_best = Operators[i].text; } } if (!best) best = sub_best; // no best found, use our plan #2 best found. Assert(best); // we better have some valid operator at this point. return best; /* char buf[256]; int child; if (tree_nodes[node].flags == EDITABLE) // data node = tree_nodes[node].parent; if (node != -1) { child = tree_nodes[node].child; if (child != -1 && tree_nodes[child].next == -1 && tree_nodes[child].child == -1) { sprintf(buf, "%s %s", tree_nodes[node].text, tree_nodes[child].text); SetItemText(tree_nodes[node].handle, buf); tree_nodes[node].flags = OPERAND | EDITABLE; tree_nodes[child].flags = COMBINED; DeleteItem(tree_nodes[child].handle); tree_nodes[child].handle = NULL; return; } }*/ } // this really only handles messages generated by the right click popup menu BOOL sexp_tree::OnCommand(WPARAM wParam, LPARAM lParam) { int i, z, id, node, op, type; sexp_list_item *list, *ptr; HTREEITEM h; if ((item_index >= 0) && (item_index < total_nodes)) item_handle = tree_nodes[item_index].handle; id = LOWORD(wParam); // Add variable if (id == ID_SEXP_TREE_ADD_VARIABLE) { CAddVariableDlg dlg; dlg.DoModal(); if ( dlg.m_create ) { // set type int type; if ( dlg.m_type_number ) { type = SEXP_VARIABLE_NUMBER; } else { type = SEXP_VARIABLE_STRING; } if ( dlg.m_type_network_variable ) { type |= SEXP_VARIABLE_NETWORK; } if ( dlg.m_type_campaign_persistent ) { type |= SEXP_VARIABLE_CAMPAIGN_PERSISTENT; } else if ( dlg.m_type_player_persistent ) { type |= SEXP_VARIABLE_PLAYER_PERSISTENT; } // add variable sexp_add_variable(dlg.m_default_value, dlg.m_variable_name, type); // sort variable sexp_variable_sort(); } return 1; } // Modify variable if (id == ID_SEXP_TREE_MODIFY_VARIABLE) { CModifyVariableDlg dlg; // get sexp_variable index for item index dlg.m_start_index = get_item_index_to_var_index(); // get pointer to tree dlg.m_p_sexp_tree = this; dlg.DoModal(); Assert( !(dlg.m_deleted && dlg.m_do_modify) ); if (dlg.m_deleted) { // find index in sexp_variable list int sexp_var_index = get_index_sexp_variable_name(dlg.m_cur_variable_name); Assert(sexp_var_index != -1); // delete from list sexp_variable_delete(sexp_var_index); // sort list sexp_variable_sort(); // delete from sexp_tree, replacing with "number" or "string" as needed // further error checking from add_data() delete_sexp_tree_variable(dlg.m_cur_variable_name); return 1; } if (dlg.m_do_modify) { // check sexp_tree -- warn on type // find index and change either (1) name, (2) type, (3) value int sexp_var_index = get_index_sexp_variable_name(dlg.m_old_var_name); Assert(sexp_var_index != -1); // save old name, since name may be modified char old_name[TOKEN_LENGTH]; strcpy(old_name, Sexp_variables[sexp_var_index].variable_name); // set type int type; if (dlg.m_type_number) { type = SEXP_VARIABLE_NUMBER; } else { type = SEXP_VARIABLE_STRING; } if ( dlg.m_type_network_variable ) { type |= SEXP_VARIABLE_NETWORK; } if ( dlg.m_type_campaign_persistent ) { type |= SEXP_VARIABLE_CAMPAIGN_PERSISTENT; } else if ( dlg.m_type_player_persistent ) { type |= SEXP_VARIABLE_PLAYER_PERSISTENT; } // update sexp_variable sexp_fred_modify_variable(dlg.m_default_value, dlg.m_cur_variable_name, sexp_var_index, type); // modify sexp_tree modify_sexp_tree_variable(old_name, sexp_var_index); FixupLoadoutNameChange(old_name, dlg.m_cur_variable_name); // Don't sort until after modify, since modify uses index if (dlg.m_modified_name) { sexp_variable_sort(); } return 1; } // no change return 1; } // check if REPLACE_VARIABLE_MENU if ( (id >= ID_VARIABLE_MENU) && (id < ID_VARIABLE_MENU + 511)) { Assert(item_index >= 0); // get index into list of type valid variables int var_idx = id - ID_VARIABLE_MENU; Assert( (var_idx >= 0) && (var_idx < MAX_SEXP_VARIABLES) ); int type = get_type(item_handle); Assert( (type & SEXPT_NUMBER) || (type & SEXPT_STRING) ); // dont do type check for modify-variable if (Modify_variable) { if (Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER) { type = SEXPT_NUMBER; } else if (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) { type = SEXPT_STRING; } else { Int3(); // unknown type } } else { // verify type in tree is same as type in Sexp_variables array if (type & SEXPT_NUMBER) { Assert(Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER); } if (type & SEXPT_STRING) { Assert( (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) ); } } // Replace data replace_variable_data(var_idx, (type | SEXPT_VARIABLE)); return 1; } if ((id >= ID_ADD_MENU) && (id < ID_ADD_MENU + 511)) { Assert(item_index >= 0); op = get_operator_index(tree_nodes[item_index].text); Assert(op >= 0); type = query_operator_argument_type(op, Add_count); list = get_listing_opf(type, item_index, Add_count); Assert(list); id -= ID_ADD_MENU; ptr = list; while (id) { id--; ptr = ptr->next; Assert(ptr); } Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0)); expand_operator(item_index); add_data(ptr->text, ptr->type); list->destroy(); return 1; } if ((id >= ID_REPLACE_MENU) && (id < ID_REPLACE_MENU + 511)) { Assert(item_index >= 0); Assert(tree_nodes[item_index].parent >= 0); op = get_operator_index(tree_nodes[tree_nodes[item_index].parent].text); Assert(op >= 0); type = query_operator_argument_type(op, Replace_count); // check argument type at this position list = get_listing_opf(type, tree_nodes[item_index].parent, Replace_count); Assert(list); id -= ID_REPLACE_MENU; ptr = list; while (id) { id--; ptr = ptr->next; Assert(ptr); } Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0)); expand_operator(item_index); replace_data(ptr->text, ptr->type); list->destroy(); return 1; } for (op=0; op= 0) h = tree_nodes[z].handle; else { h = GetParentItem(tree_nodes[item_index].handle); if (m_mode == MODE_GOALS) { Assert(Goal_editor_dlg); Goal_editor_dlg->insert_handler(item_index, node); SetItemData(h, node); } else if (m_mode == MODE_EVENTS) { Assert(Event_editor_dlg); Event_editor_dlg->insert_handler(item_index, node); SetItemData(h, node); } else if (m_mode == MODE_CAMPAIGN) { Campaign_tree_formp->insert_handler(item_index, node); SetItemData(h, node); } else { h = TVI_ROOT; root_item = node; } } item_handle = tree_nodes[node].handle = insert(Operators[op].text, BITMAP_OPERATOR, BITMAP_OPERATOR, h, tree_nodes[item_index].handle); move_branch(item_index, node); item_index = node; for (i=1; i -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)); Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST); if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) { expand_operator(item_index); replace_operator(CTEXT(Sexp_clipboard)); if (Sexp_nodes[Sexp_clipboard].rest != -1) { load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index); i = tree_nodes[item_index].child; while (i != -1) { add_sub_tree(i, tree_nodes[item_index].handle); i = tree_nodes[i].next; } } } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) { Assert(Sexp_nodes[Sexp_clipboard].rest == -1); expand_operator(item_index); replace_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID)); } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) { Assert(Sexp_nodes[Sexp_clipboard].rest == -1); expand_operator(item_index); replace_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID)); } else Assert(0); // unknown and/or invalid sexp type return 1; case ID_EDIT_PASTE_SPECIAL: // add paste, instead of replace. // the following assumptions are made.. Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)); Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST); if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) { expand_operator(item_index); add_operator(CTEXT(Sexp_clipboard)); if (Sexp_nodes[Sexp_clipboard].rest != -1) { load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index); i = tree_nodes[item_index].child; while (i != -1) { add_sub_tree(i, tree_nodes[item_index].handle); i = tree_nodes[i].next; } } } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) { Assert(Sexp_nodes[Sexp_clipboard].rest == -1); expand_operator(item_index); add_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID)); } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) { Assert(Sexp_nodes[Sexp_clipboard].rest == -1); expand_operator(item_index); add_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID)); } else Assert(0); // unknown and/or invalid sexp type return 1; /* case ID_SPLIT_LINE: if ((tree_nodes[item_index].flags & OPERAND) && (tree_nodes[item_index].flags & EDITABLE)) // expandable? expand_operator(item_index); else merge_operator(item_index); return 1;*/ case ID_EXPAND_ALL: expand_branch(item_handle); return 1; case ID_EDIT_TEXT: if (edit_label(item_handle)) { *modified = 1; EditLabel(item_handle); } return 1; case ID_ADD_STRING: { int node; node = add_data("string", (SEXPT_STRING | SEXPT_VALID)); EditLabel(tree_nodes[node].handle); return 1; } case ID_ADD_NUMBER: { int node; node = add_data("number", (SEXPT_NUMBER | SEXPT_VALID)); EditLabel(tree_nodes[node].handle); return 1; } case ID_EDIT_CUT: if (Sexp_clipboard != -1) { sexp_unmark_persistent(Sexp_clipboard); free_sexp2(Sexp_clipboard); } Sexp_clipboard = save_branch(item_index, 1); sexp_mark_persistent(Sexp_clipboard); // fall through to ID_DELETE case. case ID_DELETE: { int parent, node; HTREEITEM h; if ((m_mode & ST_ROOT_DELETABLE) && (item_index == -1)) { item_index = GetItemData(item_handle); if (m_mode == MODE_GOALS) { Assert(Goal_editor_dlg); node = Goal_editor_dlg->handler(ROOT_DELETED, item_index); } else if (m_mode == MODE_EVENTS) { Assert(Event_editor_dlg); node = Event_editor_dlg->handler(ROOT_DELETED, item_index); } else { Assert(m_mode == MODE_CAMPAIGN); node = Campaign_tree_formp->handler(ROOT_DELETED, item_index); } Assert(node >= 0); free_node2(node); DeleteItem(item_handle); *modified = 1; return 1; } Assert(item_index >= 0); h = GetParentItem(item_handle); parent = tree_nodes[item_index].parent; if ((parent == -1) && (m_mode == MODE_EVENTS)) Int3(); // no longer used, temporary to check if called still. Assert(parent != -1 && tree_nodes[parent].handle == h); free_node(item_index); DeleteItem(item_handle); node = tree_nodes[parent].child; /* if (node != -1 && tree_nodes[node].next == -1 && tree_nodes[node].child == -1) { sprintf(buf, "%s %s", tree_nodes[parent].text, tree_nodes[node].text); SetItem(h, TVIF_TEXT, buf, 0, 0, 0, 0, 0); tree_nodes[parent].flags = OPERAND | EDITABLE; tree_nodes[node].flags = COMBINED; DeleteItem(tree_nodes[node].handle); }*/ *modified = 1; return 1; } } return CTreeCtrl::OnCommand(wParam, lParam); } // adds to or replaces (based on passed in flag) the current operator void sexp_tree::add_or_replace_operator(int op, int replace_flag) { int i, op_index, op2; op_index = item_index; if (replace_flag) { if (tree_nodes[item_index].flags & OPERAND) { // are both operators? op2 = get_operator_index(tree_nodes[item_index].text); Assert(op2 >= 0); i = count_args(tree_nodes[item_index].child); if ((i >= Operators[op].min) && (i <= Operators[op].max)) { // are old num args valid? while (i--) if (query_operator_argument_type(op2, i) != query_operator_argument_type(op, i)) // does each arg match expected type? break; if (i < 0) { // everything is ok, so we can keep old arguments with new operator set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text); SetItemText(tree_nodes[item_index].handle, Operators[op].text); tree_nodes[item_index].flags = OPERAND; return; } } } replace_operator(Operators[op].text); } else add_operator(Operators[op].text); // fill in all the required (minimum) arguments with default values for (i=0; i= FIRST_OP) { // do we have an op value instead of an op number (index)? for (i=0; inext) ptr = ptr->next; ptr->next = item; item->set_op(op_num); } // add a node to end of list // Defaults: t = SEXPT_STRING // void sexp_list_item::add_data(char *str, int t) { sexp_list_item *item, *ptr; item = new sexp_list_item; ptr = this; while (ptr->next) ptr = ptr->next; ptr->next = item; item->set_data(str, t); } // add a node to end of list, allocating memory for the text // Defaults: t = SEXPT_STRING // void sexp_list_item::add_data_dup(char *str, int t) { sexp_list_item *item, *ptr; item = new sexp_list_item; ptr = this; while (ptr->next) ptr = ptr->next; ptr->next = item; item->set_data(strdup(str), t); item->flags |= SEXP_ITEM_F_DUP; } // add an sexp list to end of another list (join lists) // void sexp_list_item::add_list(sexp_list_item *list) { sexp_list_item *ptr; ptr = this; while (ptr->next) ptr = ptr->next; ptr->next = list; } // free all nodes of list // void sexp_list_item::destroy() { sexp_list_item *ptr, *ptr2; ptr = this; while (ptr) { ptr2 = ptr->next; if (ptr->flags & SEXP_ITEM_F_DUP) free(ptr->text); delete ptr; ptr = ptr2; } } int sexp_tree::add_default_operator(int op, int argnum) { char buf[256]; int index; sexp_list_item item; HTREEITEM h; h = item_handle; index = item_index; item.text = buf; if (get_default_value(&item, op, argnum)) return -1; if (item.type & SEXPT_OPERATOR) { Assert((item.op >= 0) && (item.op < Num_operators)); add_or_replace_operator(item.op); item_index = index; item_handle = h; } else { // special case for modify-variable (data added 1st arg is variable) //if ( !stricmp(Operators[op].text, "modify-variable") ) { if ( query_operator_argument_type(op, argnum) == OPF_VARIABLE_NAME) { if ((argnum == 0 && !stricmp(Operators[op].text, "modify-variable")) || (argnum == 8 && !stricmp(Operators[op].text, "add-background-bitmap")) || (argnum == 5 && !stricmp(Operators[op].text, "add-sun-bitmap"))) { int sexp_var_index = get_index_sexp_variable_name(item.text); Assert(sexp_var_index != -1); int type = SEXPT_VALID | SEXPT_VARIABLE; if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) { type |= SEXPT_STRING; } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) { type |= SEXPT_NUMBER; } else { Int3(); } char node_text[2*TOKEN_LENGTH + 2]; sprintf(node_text, "%s(%s)", item.text, Sexp_variables[sexp_var_index].text); add_variable_data(node_text, type); } else { // the the variable name char buf2[256]; Assert(argnum == 1); sexp_list_item temp_item; temp_item.text = buf2; get_default_value(&temp_item, op, 0); int sexp_var_index = get_index_sexp_variable_name(temp_item.text); Assert(sexp_var_index != -1); // from name get type int temp_type = Sexp_variables[sexp_var_index].type; int type = 0; if (temp_type & SEXP_VARIABLE_NUMBER) { type = SEXPT_VALID | SEXPT_NUMBER; } else if (temp_type & SEXP_VARIABLE_STRING) { type = SEXPT_VALID | SEXPT_STRING; } else { Int3(); } add_data(item.text, type); } } else { add_data(item.text, item.type); } } return 0; } int sexp_tree::get_default_value(sexp_list_item *item, int op, int i) { char *str = NULL; int type, index; sexp_list_item *list; HTREEITEM h; h = item_handle; index = item_index; type = query_operator_argument_type(op, i); switch (type) { case OPF_NULL: item->set_op(OP_NOP); return 0; case OPF_BOOL: item->set_op(OP_TRUE); return 0; case OPF_ANYTHING: item->set_data(""); return 0; case OPF_NUMBER: case OPF_POSITIVE: case OPF_AMBIGUOUS: // if the top level operators is an AI goal, and we are adding the last number required, // assume that this number is a priority and make it 89 instead of 1. if ((query_operator_return_type(op) == OPR_AI_GOAL) && (i == (Operators[op].min - 1))) { item->set_data("89", (SEXPT_NUMBER | SEXPT_VALID)); } else if (((Operators[op].value == OP_HAS_DOCKED_DELAY) || (Operators[op].value == OP_HAS_UNDOCKED_DELAY)) && (i == 2)) { item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID)); } else if ( (Operators[op].value == OP_SHIP_TYPE_DESTROYED) || (Operators[op].value == OP_GOOD_SECONDARY_TIME) ) { item->set_data("100", (SEXPT_NUMBER | SEXPT_VALID)); } else if ( (Operators[op].value == OP_SET_SUPPORT_SHIP) ) { item->set_data("-1", (SEXPT_NUMBER | SEXPT_VALID)); } else if ( (Operators[op].value == OP_SHIP_TAG) && (i == 1) ) { item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID)); } else if ( (Operators[op].value == OP_EXPLOSION_EFFECT) ) { int temp; char str[TOKEN_LENGTH]; switch (i) { case 3: temp = 10; break; case 4: temp = 10; break; case 5: temp = 100; break; case 6: temp = 10; break; case 7: temp = 100; break; case 10: temp = SND_SHIP_EXPLODE_1; break; case 11: temp = (int)EMP_DEFAULT_INTENSITY; break; case 12: temp = (int)EMP_DEFAULT_TIME; break; default: temp = 0; break; } // Goober5000 - set_data_dup is required if we're passing a variable sprintf(str, "%d", temp); item->set_data_dup(str, (SEXPT_NUMBER | SEXPT_VALID)); } else if ( (Operators[op].value == OP_WARP_EFFECT) ) { int temp; char str[TOKEN_LENGTH]; switch (i) { case 6: temp = 100; break; case 7: temp = 10; break; case 8: temp = SND_CAPITAL_WARP_IN; break; case 9: temp = SND_CAPITAL_WARP_OUT; break; default: temp = 0; break; } // Goober5000 - set_data_dup is required if we're passing a variable sprintf(str, "%d", temp); item->set_data_dup(str, (SEXPT_NUMBER | SEXPT_VALID)); } else if ((Operators[op].value == OP_ADD_BACKGROUND_BITMAP)) { int temp = 0; char str[TOKEN_LENGTH]; switch (i) { case 4: case 5: temp = 100; break; case 6: case 7: temp = 1; break; } sprintf(str, "%d", temp); item->set_data_dup(str, (SEXPT_NUMBER | SEXPT_VALID)); } else if ((Operators[op].value == OP_ADD_SUN_BITMAP)) { int temp = 0; char str[TOKEN_LENGTH]; if (i==4) temp = 100; sprintf(str, "%d", temp); item->set_data_dup(str, (SEXPT_NUMBER | SEXPT_VALID)); } else { item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID)); } return 0; } list = get_listing_opf(type, index, i); // Goober5000 - the way this is done is really stupid, so stupid hacks are needed to deal with it // this particular hack is necessary because the argument string should never be a default if (list && !strcmp(list->text, SEXP_ARGUMENT_STRING)) { sexp_list_item *first_ptr; first_ptr = list; list = list->next; if (first_ptr->flags & SEXP_ITEM_F_DUP) free(first_ptr->text); delete first_ptr; } if (list) { char *ptr; ptr = item->text; *item = *list; item->text = ptr; strcpy(item->text, list->text); list->destroy(); return 0; } // catch anything that doesn't have a default value. Just describe what should be here instead switch (type) { case OPF_SHIP: case OPF_SHIP_NOT_PLAYER: case OPF_SHIP_WING: case OPF_SHIP_POINT: case OPF_SHIP_WING_POINT: str = ""; break; case OPF_SHIP_OR_NONE: case OPF_SUBSYSTEM_OR_NONE: str = SEXP_NONE_STRING; break; case OPF_WING: str = ""; break; case OPF_DOCKER_POINT: str = ""; break; case OPF_DOCKEE_POINT: str = ""; break; case OPF_SUBSYSTEM: case OPF_AWACS_SUBSYSTEM: case OPF_ROTATING_SUBSYSTEM: str = ""; break; case OPF_POINT: str = ""; break; case OPF_MESSAGE: str = ""; break; case OPF_WHO_FROM: //str = ""; str = ""; break; case OPF_WAYPOINT_PATH: str = ""; break; case OPF_MISSION_NAME: str = ""; break; case OPF_GOAL_NAME: str = ""; break; case OPF_SHIP_TYPE: str = ""; break; case OPF_EVENT_NAME: str = ""; break; case OPF_HUGE_WEAPON: str = ""; break; case OPF_JUMP_NODE_NAME: str = ""; break; case OPF_NAV_POINT: str = "