Author Topic: A guide to writing sexps -- PUBLIC BETA  (Read 1389 times)

0 Members and 1 Guest are viewing this topic.

Offline The E

  • He's Ebeneezer Goode
  • Moderator
  • 213
  • Nothing personal, just tech support.
    • Steam
    • Twitter
A guide to writing sexps -- PUBLIC BETA
Okay, so, CommanderDJ asked about how to write sexps. Since I was bored, I volunteered to write a short guide. This will be posted on the wiki eventually, but for the moment, I'd like more eyes to look over it. Please point out areas that need clearing up, or parts that need to be added.

==========================================================

Writing a new sexp, a guided tour.

Hello and welcome to FSO coding. This lesson will cover the creation of new sexps.

===FILES YOU NEED TO EDIT===

1. sexp.cpp
2. sexp.h
3. (occasionally) sexp_tree.cpp
4. (also occasionally) sexp_tree.h


Okay, so you have an idea for a new sexp. Here's what you need to do in order to make it work. Note that basic understanding of C++ Syntax is needed and assumed present.

Step 1: Find out if the functionality isn't already covered by an existing sexp. There are a lot of functions already present in the sexp system, if you can use them to do what you want without adding another one, or by modifying an existing one, that's almost certainly preferable to writing a new one.

Step 2: Okay, so you've done step one and have determined that you need to do a completely new sexp. So here's the steps you need to do.
   1. In sexp.h, locate a giant list of #defines starting with "#define OP_PLUS". These are the operator constants, which are used internally to route sexp code around. Each of these defines follows a common pattern, a hex number OR'ed together with a category and (in most cases) the OP_NONCAMPAIGN_FLAG flag. Look around until you find the category where your sexp will fit in (which, in most cases, will be the "change" category), and add your flag at the bottom of the list. The hex number must follow consecutively from the last one used in that category. Then add the Category flag, and the OP_NONCAMPAIGN_FLAG (This flag is used to determine if a given sexp can be used in a campaign file to determine mission progression). If your new sexp should be present only in the campaign room, you need to use OP_CAMPAIGN_ONLY_FLAG here.
   2. In sexp.cpp, locate the Operators[] array. Each sexp has a distinct entry here. The format is { "sexp-name", OPERATOR_CONSTANT, Min number of arguments, Max number of arguments }. The sexp name string is what the sexps will later be saved as in mission files, and the documentation.
   3. In sexp.cpp, locate the function eval_sexp(int cur_node, int referenced_node). This function is called to evaluate sexps at runtime, and basically calls the sexps' evaluation function and then returns a return value. Possible return values are:
      SEXP_TRUE: This means that the sexp has executed successfully. This is the default return for all sexps, even those that do not have usable return values.
      SEXP_FALSE: This is used in sexps that return boolean values.
      SEXP_KNOWN_TRUE and SEXP_KNOWN_FALSE: These should be treated with caution. They should only be used in cases where you are absolutely certain that a given result will stay true or false for the entire mission. Examples are found in the is-destroyed sexp for example, which returns SEXP_KNOWN_FALSE if the ship or wing passed as an argument has departed (warped out). The danger here is that once you return a KNOWN value, the sexp system will stop evaluating that specific expression.
      SEXP_UNKNOWN: This is the default value any given sexp tree node has before evaluation starts.
      SEXP_NAN and SEXP_NAN_FOREVER: Should be returned when an operation is supposed to return a number, but can't for whatever reason. SEXP_NAN_FOREVER acts much like the SEXP_KNOWN return values; using it means that a given operation will always return NAN. As an example, if you use get-object-speed on a departed or destroyed object, it will return SEXP_NAN_FOREVER.
      SEXP_CANT_EVAL: Used when the sexp cannot be evaluated yet for whatever reason. For example, this is returned when is-destroyed is used on a ship that hasn't arrived yet.
   These values can and should be returned from your evaluation function, if necessary. It is here that you should insert a call to your evaluation function, with the current node as an argument.
   4. In sexp.cpp, locate the query_operator_return_type(int op) function. In it, you will find a massive switch statement. This is used to determine what your sexp returns, which is in turn used by the sexp syntax checker to determine if your sexp is placed correctly, and by FRED to determine which sexps are available in a given situation. Possible values are:
      OPR_NULL: This sexp does not return anything
      OPR_BOOL: This sexp returns a boolean value
      OPR_NUMBER: This sexp returns a number
      OPR_POSITIVE: This sexp returns a positive number
   There are a few special return types as well. These are used for type matching (that is, to see if a given sexp is valid at a given point in the expression)
      OPR_AI_GOAL, OPR_STRING, OPR_AMBIGUOUS, OPR_FLEXIBLE_ARGUMENT
   5. In sexp.cpp, locate the query_operator_argument_type(int op, int argnum) function. This is similar to query_operator_return_type(), in that it is used to check sexp syntax. If your sexp has more than one argument, or more than one type of argument, you need to use the argnum variable to determine what you need to return. Possible return values are found in sexp.h, look for the block of OPF_* defines.
   6. In sexp.cpp, locate the get_subcategory(int sexp_id) function. Locate the subcategory where your sexp will fit in best, and add your operator constant to the switch statement there.
   7. In sexp.cpp, locate the Sexp_help[] array. This is where you need to add a short description of your sexp, which will appear in the event editor. Syntax is simply { OPERATOR_CONSTANT, "help text" }. Look at the other entries, and follow the formatting found there.
   
   Okay, so that's the whole setup out of the way. Now we'll have to write the evaluation function.
   Almost all evaluation functions have the current node in the sexp tree as an argument. This is used to retrieve arguments given to the sexp by the FREDer. To retrieve arguments from a node, the following helper functions and macros exist:
      CDR(n): Returns the next branch in the tree
      CAR(n): Returns the root node of the current element
      CTEXT(n): Returns a c-string representation of a text argument.
      eval_num(n): Returns a numeric argument
      is_sexp_true(n): Evaluates boolean arguments. Returns true if the expression is true or KNOWN_TRUE.
      sexp_get_ship_from_node(n): Returns a pointer to the Ship pointer, or NULL if the ship can'T be found in the mission.
      ship_name_lookup(CTEXT(n)): Returns the ships' position in the Ships[] array.
      ship_type_name_lookup(CTEXT(n)): Returns the ship class' position the Ship_info[] array.
      
   
Note that, at this time, this guide does not explain how to set up sexps for multiplayer. Use Karajorma's guide for this
If I'm just aching this can't go on
I came from chasing dreams to feel alone
There must be changes, miss to feel strong
I really need lifе to touch me
--Evergrey, Where August Mourns