Files
fs2open.github.com/code/fred2/sexp_tree.cpp
T
MageKing17 9a5fa95c54 Add subspace beam strikes.
Overhaul hudartillery.cpp to use STL containers (removes hardcoded limits on maximum number of strike types, simultaneously active strikes, and projectiles per strike), use modular tables, allow for different shape options, and allow beams as the weapon.
2015-05-15 16:19:44 -07:00

6199 lines
151 KiB
C++
Executable File

/*
* 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.
*
*/
#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"
#include "mission/missionmessage.h"
#include "graphics/gropenglshader.h"
#include "graphics/gropenglpostprocessing.h"
#include "sound/ds.h"
#include "globalincs/alphacolors.h"
#include "localization/localize.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
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//********************sexp_tree********************
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(const 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<tree_nodes.size(); i++)
tree_nodes[i].handle = NULL;
}
// initializes and creates a tree from a given sexp startpoint.
void sexp_tree::load_tree(int index, const char *deflt)
{
int cur;
clear_tree();
root_item = 0;
if (index < 0) {
cur = allocate_node(-1);
set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), deflt); // setup a default tree if none
build_tree();
return;
}
if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) { // handle numbers allender likes to use so much..
cur = allocate_node(-1);
if (atoi(Sexp_nodes[index].text))
set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "true");
else
set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "false");
build_tree();
return;
}
// 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);
load_branch(index, -1);
build_tree();
}
void get_combined_variable_name(char *combined_name, const char *sexp_var_name)
{
int sexp_var_index = get_index_sexp_variable_name(sexp_var_name);
Assert(sexp_var_index > -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 {
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 (int i = old_size; i < (int)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_s(tree_nodes[node].text, "<uninitialized tree node>");
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, const 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_s(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 = get_data_image(node);
}
}
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 {
int bmap = get_data_image(node);
tree_nodes[node].handle = insert(tree_nodes[node].text, bmap, bmap, 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, const 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; i<tree_nodes.size(); i++)
if (tree_nodes[i].handle == item_handle) {
item_index = i;
break;
}
}
// handler for right mouse button clicks.
void sexp_tree::right_clicked(int mode)
{
char buf[256];
int i, j, z, count, op, add_type, replace_type, type, subcategory_id;
sexp_list_item *list;
UINT _flags;
HTREEITEM h;
POINT click_point, mouse;
CMenu menu, *mptr, *popup_menu, *add_data_menu = NULL, *replace_data_menu = NULL;
CMenu *add_op_menu, add_op_submenu[MAX_OP_MENUS];
CMenu *replace_op_menu, replace_op_submenu[MAX_OP_MENUS];
CMenu *insert_op_menu, insert_op_submenu[MAX_OP_MENUS];
CMenu *replace_variable_menu = NULL;
CMenu add_op_subcategory_menu[MAX_SUBMENUS];
CMenu replace_op_subcategory_menu[MAX_SUBMENUS];
CMenu insert_op_subcategory_menu[MAX_SUBMENUS];
m_mode = mode;
add_instance = replace_instance = -1;
Assert(Num_operators <= MAX_OPERATORS);
Assert(Num_op_menus < MAX_OP_MENUS);
Assert(Num_submenus < MAX_SUBMENUS);
GetCursorPos(&mouse);
click_point = mouse;
ScreenToClient(&click_point);
h = HitTest(CPoint(click_point), &_flags); // find out what they clicked on
if (h && menu.LoadMenu(IDR_MENU_EDIT_SEXP_TREE)) {
update_help(h);
popup_menu = menu.GetSubMenu(0);
ASSERT(popup_menu != NULL);
//SelectDropTarget(h); // WTF: Why was this here???
add_op_menu = replace_op_menu = insert_op_menu = NULL;
// get pointers to several key popup menus we'll need to modify
i = popup_menu->GetMenuItemCount();
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; i<Num_op_menus; i++)
{
add_op_submenu[i].CreatePopupMenu();
replace_op_submenu[i].CreatePopupMenu();
insert_op_submenu[i].CreatePopupMenu();
add_op_menu->AppendMenu(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 don't 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)
{
int modify_type = get_modify_variable_type(parent);
if (modify_type == OPF_NUMBER) {
type = SEXPT_NUMBER;
} else if (modify_type == OPF_AMBIGUOUS) {
type = SEXPT_STRING;
} else {
Int3();
type = tree_nodes[first_arg].type;
}
}
// Goober5000 - certain types accept both integers and a list of strings
if (op_type == OPF_GAME_SND || op_type == OPF_WEAPON_BANK_NUMBER)
{
type = SEXPT_NUMBER | SEXPT_STRING;
}
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; idx<max_sexp_vars; idx++) {
if (Sexp_variables[idx].type & SEXP_VARIABLE_SET) {
// skip block variables
if (Sexp_variables[idx].type & SEXP_VARIABLE_BLOCK) {
continue;
}
UINT flag = MF_STRING | MF_GRAYED;
// maybe gray flag MF_GRAYED
// get type -- gray "string" or number accordingly
if ( type & SEXPT_STRING ) {
if ( Sexp_variables[idx].type & SEXP_VARIABLE_STRING ) {
flag &= ~MF_GRAYED;
}
}
if ( type & SEXPT_NUMBER ) {
if ( Sexp_variables[idx].type & SEXP_VARIABLE_NUMBER ) {
flag &= ~MF_GRAYED;
}
}
// if modify-variable and changing variable, enable all variables
if (op_type == OPF_VARIABLE_NAME) {
Modify_variable = 1;
flag &= ~MF_GRAYED;
} else {
Modify_variable = 0;
}
// enable navsystem always
if (op_type == OPF_NAV_POINT)
flag &= ~MF_GRAYED;
if (!( (idx + 3) % 30)) {
flag |= MF_MENUBARBREAK;
}
char buf[128];
// append list of variable names and values
// set id as ID_VARIABLE_MENU + idx
sprintf(buf, "%s (%s)", Sexp_variables[idx].variable_name, Sexp_variables[idx].text);
replace_variable_menu->AppendMenu(flag, (ID_VARIABLE_MENU + idx), buf);
}
}
}
//}
}
}
// can't 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; i<Num_submenus; i++)
{
add_op_subcategory_menu[i].CreatePopupMenu();
replace_op_subcategory_menu[i].CreatePopupMenu();
insert_op_subcategory_menu[i].CreatePopupMenu();
for (j=0; j<Num_op_menus; j++)
{
if (op_menu[j].id == category_of_subcategory(op_submenu[i].id))
{
add_op_submenu[j].AppendMenu(MF_POPUP, (UINT) add_op_subcategory_menu[i].m_hMenu, op_submenu[i].name);
replace_op_submenu[j].AppendMenu(MF_POPUP, (UINT) replace_op_subcategory_menu[i].m_hMenu, op_submenu[i].name);
insert_op_submenu[j].AppendMenu(MF_POPUP, (UINT) insert_op_subcategory_menu[i].m_hMenu, op_submenu[i].name);
break; // only 1 category valid
}
}
}
// add operator menu items to the various CATEGORY submenus they belong in
for (i=0; i<Num_operators; i++)
{
// add only if it is not in a subcategory
subcategory_id = get_subcategory(Operators[i].value);
if (subcategory_id == -1)
{
// put it in the appropriate menu
for (j=0; j<Num_op_menus; j++)
{
if (op_menu[j].id == get_category(Operators[i].value))
{
switch (Operators[i].value) {
// Commented out by Goober5000 to allow these operators to be selectable
/*#ifdef NDEBUG
// various campaign operators
case OP_WAS_PROMOTION_GRANTED:
case OP_WAS_MEDAL_GRANTED:
case OP_GRANT_PROMOTION:
case OP_GRANT_MEDAL:
case OP_TECH_ADD_SHIP:
case OP_TECH_ADD_WEAPON:
case OP_TECH_ADD_INTEL_XSTR:
case OP_TECH_RESET_TO_DEFAULT:
#endif*/
// unlike the above operators, these are deprecated
case OP_HITS_LEFT_SUBSYSTEM:
case OP_CUTSCENES_SHOW_SUBTITLE:
case OP_ORDER:
case OP_TECH_ADD_INTEL:
case OP_HUD_GAUGE_SET_ACTIVE:
case OP_HUD_ACTIVATE_GAUGE_TYPE:
j = Num_op_menus; // don't allow these operators to be visible
break;
}
if (j < Num_op_menus) {
add_op_submenu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value, Operators[i].text);
replace_op_submenu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value | OP_REPLACE_FLAG, Operators[i].text);
insert_op_submenu[j].AppendMenu(MF_STRING, Operators[i].value | OP_INSERT_FLAG, Operators[i].text);
}
break; // only 1 category valid
}
}
}
// if it is in a subcategory, handle it
else
{
// put it in the appropriate submenu
for (j=0; j<Num_submenus; j++)
{
if (op_submenu[j].id == subcategory_id)
{
switch (Operators[i].value) {
// Commented out by Goober5000 to allow these operators to be selectable
/*#ifdef NDEBUG
// various campaign operators
case OP_WAS_PROMOTION_GRANTED:
case OP_WAS_MEDAL_GRANTED:
case OP_GRANT_PROMOTION:
case OP_GRANT_MEDAL:
case OP_TECH_ADD_SHIP:
case OP_TECH_ADD_WEAPON:
case OP_TECH_ADD_INTEL_XSTR:
case OP_TECH_RESET_TO_DEFAULT:
#endif*/
// unlike the above operators, these are deprecated
case OP_HITS_LEFT_SUBSYSTEM:
case OP_CUTSCENES_SHOW_SUBTITLE:
case OP_ORDER:
case OP_TECH_ADD_INTEL:
case OP_HUD_GAUGE_SET_ACTIVE:
case OP_HUD_ACTIVATE_GAUGE_TYPE:
j = Num_submenus; // don't allow these operators to be visible
break;
}
if (j < Num_submenus) {
add_op_subcategory_menu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value, Operators[i].text);
replace_op_subcategory_menu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value | OP_REPLACE_FLAG, Operators[i].text);
insert_op_subcategory_menu[j].AppendMenu(MF_STRING, Operators[i].value | OP_INSERT_FLAG, Operators[i].text);
}
break; // only 1 subcategory valid
}
}
}
}
// find local index (i) of current item (from its handle)
SelectItem(item_handle = h);
for (i=0; i<(int)tree_nodes.size(); i++) {
if (tree_nodes[i].handle == h) {
break;
}
}
// special case: item is a ROOT node, and a label that can be edited (not an item in the sexp tree)
if ((item_index == -1) && (m_mode & ST_LABELED_ROOT)) {
if (m_mode & ST_ROOT_EDITABLE) {
menu.EnableMenuItem(ID_EDIT_TEXT, MF_ENABLED);
} else {
menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED);
}
// disable copy, insert op
menu.EnableMenuItem(ID_EDIT_COPY, MF_GRAYED);
for (j=0; j<Num_operators; j++) {
menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
}
gray_menu_tree(popup_menu);
popup_menu->TrackPopupMenu(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<Num_operators; j++) {
if (!query_default_argument_available(j)) {
menu.EnableMenuItem(Operators[j].value, MF_GRAYED);
}
}
// change enabled status of 'replace' type menu options.
replace_type = 0;
int parent = tree_nodes[item_index].parent;
if (parent >= 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; i<count; i++) {
if (query_operator_argument_type(op, i-1) != query_operator_argument_type(op, i)) {
menu.EnableMenuItem(ID_DELETE, MF_GRAYED);
break;
}
}
type = query_operator_argument_type(op, Replace_count); // check argument type at this position
// special case reset type for ambiguous
if (type == OPF_AMBIGUOUS) {
type = get_modify_variable_type(parent);
}
list = get_listing_opf(type, parent, Replace_count);
// special case don't allow replace data for variable names
if ( (type != OPF_VARIABLE_NAME) && list) {
sexp_list_item *ptr;
int data_idx = 0;
ptr = list;
while (ptr) {
if (ptr->op >= 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;
} else if (type == OPF_AI_GOAL) {
replace_type = OPR_AI_GOAL;
}
// Goober5000
else if (type == OPF_FLEXIBLE_ARGUMENT) {
replace_type = OPR_FLEXIBLE_ARGUMENT;
}
// Goober5000
else if (type == OPF_GAME_SND || type == OPF_WEAPON_BANK_NUMBER) {
// enable number even though we are also going to default to string
menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
}
// default to string
if (replace_type == OPR_STRING) {
menu.EnableMenuItem(ID_REPLACE_STRING, MF_ENABLED);
}
// modify string or number if (modify_variable)
if ( Operators[op].value == OP_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
}
else if ( Operators[op].value == OP_SET_VARIABLE_BY_INDEX ) {
// it depends on which argument we are modifying
// first argument is always a number
if (Replace_count == 0) {
menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
menu.EnableMenuItem(ID_REPLACE_STRING, MF_GRAYED);
}
// second argument could be anything
else {
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<Num_operators; j++)
if (query_operator_return_type(j) == OPR_NULL)
menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_ENABLED);
} else {
replace_type = OPR_BOOL;
for (j=0; j<Num_operators; j++)
if (query_operator_return_type(j) == OPR_BOOL)
menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_ENABLED);
}
}
// disable operators that do not have arguments available
for (j=0; j<Num_operators; j++) {
if (!query_default_argument_available(j)) {
menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_GRAYED);
}
}
// change enabled status of 'insert' type menu options.
z = tree_nodes[item_index].parent;
Assert(z >= -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<Num_operators; j++) {
z = query_operator_return_type(j);
if (!sexp_query_type_match(type, z) || (Operators[j].min < 1))
menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
z = query_operator_argument_type(j, 0);
if ((type == OPF_NUMBER) && (z == OPF_POSITIVE))
z = OPF_NUMBER;
// Goober5000's number hack
if ((type == OPF_POSITIVE) && (z == OPF_NUMBER))
z = OPF_POSITIVE;
if (z != type)
menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
}
// disable operators that do not have arguments available
for (j=0; j<Num_operators; j++) {
if (!query_default_argument_available(j)) {
menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
}
}
// disable non campaign operators if in campaign mode
for (j=0; j<Num_operators; j++) {
z = 0;
if (m_mode == MODE_CAMPAIGN) {
if (Operators[j].value & OP_NONCAMPAIGN_FLAG)
z = 1;
} else {
if (Operators[j].value & OP_CAMPAIGN_ONLY_FLAG)
z = 1;
}
if (z) {
menu.EnableMenuItem(Operators[j].value, MF_GRAYED);
menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_GRAYED);
menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
}
}
if ((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) {
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; i<tree_nodes.size(); i++) {
if (tree_nodes[i].handle == h) {
break;
}
}
// Check if tree root
if (i == tree_nodes.size()) {
if (m_mode & ST_ROOT_EDITABLE) {
return 1;
}
return 0;
}
// Operators are editable
if (tree_nodes[i].type & SEXPT_OPERATOR) {
return 1;
}
// Variables must be edited through dialog box
if (tree_nodes[i].type & SEXPT_VARIABLE) {
return 0;
}
// Don't edit if not flaged as editable
if (!(tree_nodes[i].flags & EDITABLE)) {
return 0;
}
// Otherwise, allow editing
return 1;
/*
if (tree_nodes[i].flags & OPERAND) {
data = tree_nodes[i].child;
SetItemText(h, tree_nodes[i].text);
tree_nodes[i].flags = OPERAND;
item_handle = tree_nodes[data].handle = insert(tree_nodes[data].text, tree_nodes[data].type, tree_nodes[data].flags, h);
tree_nodes[data].flags = EDITABLE;
Expand(h, TVE_EXPAND);
SelectItem(item_handle);
return 2;
}
*/
}
// given a tree node, returns the argument type it should be.
int sexp_tree::query_node_argument_type(int node)
{
int argnum = 0;
int parent_node = tree_nodes[node].parent;
Assert(parent_node >= 0);
argnum = find_argument_number(parent_node, node);
int op_num = get_operator_index(tree_nodes[parent_node].text);
return query_operator_argument_type(op_num, argnum);
}
int sexp_tree::end_label_edit(TVITEMA &item)
{
HTREEITEM h = item.hItem;
char *str = item.pszText;
int len, r = 1;
bool update_node = true;
uint node;
*modified = 1;
if (!str)
return 0;
// let's make sure we aren't introducing any invalid characters, per Mantis #2893
lcl_fred_replace_stuff(str, TOKEN_LENGTH - 1);
for (node=0; node<tree_nodes.size(); node++)
if (tree_nodes[node].handle == h)
break;
if (node == tree_nodes.size()) {
if (m_mode == MODE_EVENTS) {
item_index = GetItemData(h);
Assert(Event_editor_dlg);
node = Event_editor_dlg->handler(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) {
const char *op = match_closest_operator(str, node);
if (!op) return 0; // Goober5000 - avoids crashing
SetItemText(h, op);
item_index = node;
int op_num = get_operator_index(op);
if (op_num >= 0 ) {
add_or_replace_operator(op_num, 1);
}
else {
update_node = false;
}
r = 0;
}
// gotta sidestep Goober5000's number hack and check entries are actually positive.
else if (tree_nodes[node].type & SEXPT_NUMBER) {
if (query_node_argument_type(node) == OPF_POSITIVE) {
int val = atoi(str);
if (val < 0) {
MessageBox("Can not enter a negative value", "Invalid Number", MB_ICONEXCLAMATION);
update_node = false;
}
}
}
// Error checking would not hurt here
len = strlen(str);
if (len >= TOKEN_LENGTH)
len = TOKEN_LENGTH - 1;
if (update_node) {
strncpy(tree_nodes[node].text, str, len);
tree_nodes[node].text[len] = 0;
}
else {
strncpy(str, tree_nodes[node].text, len);
return 1;
}
/* 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'
//
const char *sexp_tree::match_closest_operator(const char *str, int node)
{
int z, i, op, arg_num, opf, opr;
const 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, node);
opf = query_operator_argument_type(op, arg_num); // check argument type at this position
opr = query_operator_return_type(op);
for (i=0; i<Num_operators; i++) {
if (sexp_query_type_match(opf, opr)) {
if ( (stricmp(str, Operators[i].text) <= 0) && (!best || (stricmp(str, best) < 0)) )
best = Operators[i].text;
if ( !sub_best || (stricmp(Operators[i].text, sub_best) > 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_s(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);
// 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) );
// don't 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<Num_operators; op++) {
if (id == Operators[op].value) {
add_or_replace_operator(op);
return 1;
}
if (id == (Operators[op].value | OP_REPLACE_FLAG)) {
add_or_replace_operator(op, 1);
expand_branch(item_handle);
return 1;
}
if (id == (Operators[op].value | OP_INSERT_FLAG)) {
int flags;
z = tree_nodes[item_index].parent;
flags = tree_nodes[item_index].flags;
node = allocate_node(z, item_index);
set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text);
tree_nodes[node].flags = flags;
if (z >= 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<Operators[op].min; i++)
add_default_operator(op, i);
Expand(item_handle, TVE_EXPAND);
*modified = 1;
return 1;
}
}
switch (id) {
case ID_EDIT_COPY:
// If a clipboard already exist, unmark it as persistent and free old clipboard
if (Sexp_clipboard != -1) {
sexp_unmark_persistent(Sexp_clipboard);
free_sexp2(Sexp_clipboard);
}
// Allocate new clipboard and mark persistent
Sexp_clipboard = save_branch(item_index, 1);
sexp_mark_persistent(Sexp_clipboard);
return 1;
case ID_EDIT_PASTE:
// 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);
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);
if (Sexp_nodes[Sexp_clipboard].type & SEXP_FLAG_VARIABLE) {
int var_idx = get_index_sexp_variable_name(Sexp_nodes[Sexp_clipboard].text);
Assert(var_idx > -1);
replace_variable_data(var_idx, (SEXPT_VARIABLE | SEXPT_NUMBER | SEXPT_VALID));
}
else {
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);
if (Sexp_nodes[Sexp_clipboard].type & SEXP_FLAG_VARIABLE) {
int var_idx = get_index_sexp_variable_name(Sexp_nodes[Sexp_clipboard].text);
Assert(var_idx > -1);
replace_variable_data(var_idx, (SEXPT_VARIABLE | SEXPT_STRING | SEXPT_VALID));
}
else {
expand_operator(item_index);
replace_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
}
} else
Assert(0); // unknown and/or invalid sexp type
expand_branch(item_handle);
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
expand_branch(item_handle);
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_REPLACE_NUMBER:
expand_operator(item_index);
replace_data("number", (SEXPT_NUMBER | SEXPT_VALID));
EditLabel(tree_nodes[item_index].handle);
return 1;
case ID_REPLACE_STRING:
expand_operator(item_index);
replace_data("string", (SEXPT_STRING | SEXPT_VALID));
EditLabel(tree_nodes[item_index].handle);
return 1;
case ID_ADD_STRING: {
int theNode;
theNode = add_data("string", (SEXPT_STRING | SEXPT_VALID));
EditLabel(tree_nodes[theNode].handle);
return 1;
}
case ID_ADD_NUMBER: {
int theNode;
theNode = add_data("number", (SEXPT_NUMBER | SEXPT_VALID));
EditLabel(tree_nodes[theNode].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, theNode;
HTREEITEM h_parent;
if ((m_mode & ST_ROOT_DELETABLE) && (item_index == -1)) {
item_index = GetItemData(item_handle);
if (m_mode == MODE_GOALS) {
Assert(Goal_editor_dlg);
theNode = Goal_editor_dlg->handler(ROOT_DELETED, item_index);
} else if (m_mode == MODE_EVENTS) {
Assert(Event_editor_dlg);
theNode = Event_editor_dlg->handler(ROOT_DELETED, item_index);
} else {
Assert(m_mode == MODE_CAMPAIGN);
theNode = Campaign_tree_formp->handler(ROOT_DELETED, item_index);
}
Assert(theNode >= 0);
free_node2(theNode);
DeleteItem(item_handle);
*modified = 1;
return 1;
}
Assert(item_index >= 0);
h_parent = 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_parent);
free_node(item_index);
DeleteItem(item_handle);
theNode = 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_parent, 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<Operators[op].min; i++)
add_default_operator(op, i);
Expand(item_handle, TVE_EXPAND);
}
// initialize node, type operator
//
void sexp_list_item::set_op(int op_num)
{
int i;
if (op_num >= FIRST_OP) { // do we have an op value instead of an op number (index)?
for (i=0; i<Num_operators; i++)
if (op_num == Operators[i].value)
op_num = i; // convert op value to op number
}
op = op_num;
text = Operators[op].text;
type = (SEXPT_OPERATOR | SEXPT_VALID);
}
// initialize node, type data
// Defaults: t = SEXPT_STRING
//
void sexp_list_item::set_data(const char *str, int t)
{
op = -1;
text = str;
type = t;
}
// initialize node, type data, allocating memory for the text
// Defaults: t = SEXPT_STRING
//
void sexp_list_item::set_data_dup(const char *str, int t)
{
op = -1;
text = strdup(str);
flags |= SEXP_ITEM_F_DUP;
type = t;
}
// add a node to end of list
//
void sexp_list_item::add_op(int op_num)
{
sexp_list_item *item, *ptr;
item = new sexp_list_item;
ptr = this;
while (ptr->next)
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(const 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(const 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((void *) 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;
if (get_default_value(&item, buf, 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 && Operators[op].value == OP_MODIFY_VARIABLE) ||
(argnum == 8 && Operators[op].value == OP_ADD_BACKGROUND_BITMAP) ||
(argnum == 5 && Operators[op].value == OP_ADD_SUN_BITMAP) ||
(argnum == 2 && Operators[op].value == OP_STRING_CONCATENATE) ||
(argnum == 1 && Operators[op].value == OP_INT_TO_STRING) ||
(argnum == 3 && Operators[op].value == OP_STRING_GET_SUBSTRING) ||
(argnum == 4 && Operators[op].value == OP_STRING_SET_SUBSTRING) ||
(argnum == 1 && Operators[op].value == OP_COPY_VARIABLE_FROM_INDEX) ||
(argnum == 1 && Operators[op].value == OP_SCRIPT_EVAL_STRING))
{
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;
get_default_value(&temp_item, buf2, 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, char *text_buf, 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("<any 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) || (Operators[op].value == OP_TRIGGER_SUBMODEL_ANIMATION) && (i == 3) )
{
item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
}
else if ( (Operators[op].value == OP_EXPLOSION_EFFECT) )
{
int temp;
char sexp_str_token[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 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(sexp_str_token, "%d", temp);
item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
}
else if ( (Operators[op].value == OP_WARP_EFFECT) )
{
int temp;
char sexp_str_token[TOKEN_LENGTH];
switch (i)
{
case 6:
temp = 100;
break;
case 7:
temp = 10;
break;
default:
temp = 0;
break;
}
// Goober5000 - set_data_dup is required if we're passing a variable
sprintf(sexp_str_token, "%d", temp);
item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
}
else if ((Operators[op].value == OP_ADD_BACKGROUND_BITMAP))
{
int temp = 0;
char sexp_str_token[TOKEN_LENGTH];
switch (i)
{
case 4:
case 5:
temp = 100;
break;
case 6:
case 7:
temp = 1;
break;
}
sprintf(sexp_str_token, "%d", temp);
item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
}
else if ((Operators[op].value == OP_ADD_SUN_BITMAP))
{
int temp = 0;
char sexp_str_token[TOKEN_LENGTH];
if (i==4) temp = 100;
sprintf(sexp_str_token, "%d", temp);
item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
}
else if ((Operators[op].value == OP_MODIFY_VARIABLE)) {
if (get_modify_variable_type(index) == OPF_NUMBER) {
item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
}
else {
item->set_data("<any data>", (SEXPT_STRING | SEXPT_VALID));
}
}
else if ((Operators[op].value == OP_SET_VARIABLE_BY_INDEX)) {
if (i == 0) {
item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
}
else {
item->set_data("<any data>", (SEXPT_STRING | SEXPT_VALID));
}
}
else
{
item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
}
return 0;
// Goober5000 - special cases that used to be numbers but are now hybrids
case OPF_GAME_SND:
int sound_index = -1;
if ( (Operators[op].value == OP_EXPLOSION_EFFECT) )
{
sound_index = SND_SHIP_EXPLODE_1;
}
else if ( (Operators[op].value == OP_WARP_EFFECT) )
{
sound_index = (i == 8) ? SND_CAPITAL_WARP_IN : SND_CAPITAL_WARP_OUT;
}
if (sound_index >= 0)
{
game_snd *snd = &Snds[sound_index];
if (can_construe_as_integer(snd->name.c_str()))
item->set_data(snd->name.c_str(), (SEXPT_NUMBER | SEXPT_VALID));
else
item->set_data(snd->name.c_str(), (SEXPT_STRING | SEXPT_VALID));
return 0;
}
// if no hardcoded default, just use the listing default
break;
}
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((void *) first_ptr->text);
delete first_ptr;
}
if (list)
{
// copy the information from the list to the passed-in item
*item = *list;
// but use the provided text buffer
strcpy(text_buf, list->text);
item->text = text_buf;
// get rid of the list, since we're done with it
list->destroy();
item->next = NULL;
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_POINT:
case OPF_SHIP_WING:
case OPF_SHIP_WING_WHOLETEAM:
case OPF_SHIP_WING_SHIPONTEAM_POINT:
case OPF_SHIP_WING_POINT:
str = "<name of ship here>";
break;
case OPF_ORDER_RECIPIENT:
str = "<all fighters>";
break;
case OPF_SHIP_OR_NONE:
case OPF_SUBSYSTEM_OR_NONE:
case OPF_SHIP_WING_POINT_OR_NONE:
str = SEXP_NONE_STRING;
break;
case OPF_WING:
str = "<name of wing here>";
break;
case OPF_DOCKER_POINT:
str = "<docker point>";
break;
case OPF_DOCKEE_POINT:
str = "<dockee point>";
break;
case OPF_SUBSYSTEM:
case OPF_AWACS_SUBSYSTEM:
case OPF_ROTATING_SUBSYSTEM:
case OPF_SUBSYS_OR_GENERIC:
str = "<name of subsystem>";
break;
case OPF_SUBSYSTEM_TYPE:
str = Subsystem_types[SUBSYSTEM_NONE];
break;
case OPF_POINT:
str = "<waypoint>";
break;
case OPF_MESSAGE:
str = "<Message>";
break;
case OPF_WHO_FROM:
//str = "<any allied>";
str = "<any wingman>";
break;
case OPF_WAYPOINT_PATH:
str = "<waypoint path>";
break;
case OPF_MISSION_NAME:
str = "<mission name>";
break;
case OPF_GOAL_NAME:
str = "<goal name>";
break;
case OPF_SHIP_TYPE:
str = "<ship type here>";
break;
case OPF_EVENT_NAME:
str = "<event name>";
break;
case OPF_HUGE_WEAPON:
str = "<huge weapon type>";
break;
case OPF_JUMP_NODE_NAME:
str = "<Jump node name>";
break;
case OPF_NAV_POINT:
str = "<Nav 1>";
break;
case OPF_ANYTHING:
str = "<any data>";
break;
case OPF_PERSONA:
str = "<persona name>";
break;
case OPF_FONT:
str = Fonts[0].filename;
break;
case OPF_AUDIO_VOLUME_OPTION:
str = "Music";
break;
case OPF_POST_EFFECT:
str = "<Effect Name>";
break;
case OPF_HUD_GAUGE:
str = "Messages";
break;
default:
str = "<new default required!>";
break;
}
item->set_data(str, (SEXPT_STRING | SEXPT_VALID));
return 0;
}
int sexp_tree::query_default_argument_available(int op)
{
int i;
Assert(op >= 0);
for (i=0; i<Operators[op].min; i++)
if (!query_default_argument_available(op, i))
return 0;
return 1;
}
int sexp_tree::query_default_argument_available(int op, int i)
{
int j, type;
object *ptr;
type = query_operator_argument_type(op, i);
switch (type) {
case OPF_NONE:
case OPF_NULL:
case OPF_BOOL:
case OPF_NUMBER:
case OPF_POSITIVE:
case OPF_IFF:
case OPF_AI_CLASS:
case OPF_WHO_FROM:
case OPF_PRIORITY:
case OPF_SHIP_TYPE:
case OPF_SUBSYSTEM:
case OPF_AWACS_SUBSYSTEM:
case OPF_ROTATING_SUBSYSTEM:
case OPF_SUBSYSTEM_TYPE:
case OPF_DOCKER_POINT:
case OPF_DOCKEE_POINT:
case OPF_AI_GOAL:
case OPF_KEYPRESS:
case OPF_AI_ORDER:
case OPF_SKILL_LEVEL:
case OPF_MEDAL_NAME:
case OPF_WEAPON_NAME:
case OPF_INTEL_NAME:
case OPF_SHIP_CLASS_NAME:
case OPF_HUD_GAUGE_NAME:
case OPF_HUGE_WEAPON:
case OPF_JUMP_NODE_NAME:
case OPF_AMBIGUOUS:
case OPF_CARGO:
case OPF_ARRIVAL_LOCATION:
case OPF_DEPARTURE_LOCATION:
case OPF_ARRIVAL_ANCHOR_ALL:
case OPF_SUPPORT_SHIP_CLASS:
case OPF_SHIP_WITH_BAY:
case OPF_SOUNDTRACK_NAME:
case OPF_STRING:
case OPF_FLEXIBLE_ARGUMENT:
case OPF_ANYTHING:
case OPF_SKYBOX_MODEL_NAME:
case OPF_SKYBOX_FLAGS:
case OPF_SHIP_OR_NONE:
case OPF_SUBSYSTEM_OR_NONE:
case OPF_SHIP_WING_POINT_OR_NONE:
case OPF_SUBSYS_OR_GENERIC:
case OPF_BACKGROUND_BITMAP:
case OPF_SUN_BITMAP:
case OPF_NEBULA_STORM_TYPE:
case OPF_NEBULA_POOF:
case OPF_TURRET_TARGET_ORDER:
case OPF_POST_EFFECT:
case OPF_TARGET_PRIORITIES:
case OPF_ARMOR_TYPE:
case OPF_DAMAGE_TYPE:
case OPF_FONT:
case OPF_HUD_ELEMENT:
case OPF_SOUND_ENVIRONMENT:
case OPF_SOUND_ENVIRONMENT_OPTION:
case OPF_EXPLOSION_OPTION:
case OPF_AUDIO_VOLUME_OPTION:
case OPF_WEAPON_BANK_NUMBER:
case OPF_MESSAGE_OR_STRING:
case OPF_HUD_GAUGE:
case OPF_SHIP_EFFECT:
case OPF_ANIMATION_TYPE:
case OPF_SHIP_FLAG:
case OPF_NEBULA_PATTERN:
case OPF_NAV_POINT:
case OPF_TEAM_COLOR:
case OPF_GAME_SND:
return 1;
case OPF_SHIP:
case OPF_SHIP_WING:
case OPF_SHIP_POINT:
case OPF_SHIP_WING_POINT:
case OPF_SHIP_WING_WHOLETEAM:
case OPF_SHIP_WING_SHIPONTEAM_POINT:
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (ptr->type == OBJ_SHIP || ptr->type == OBJ_START)
return 1;
ptr = GET_NEXT(ptr);
}
return 0;
case OPF_SHIP_NOT_PLAYER:
case OPF_ORDER_RECIPIENT:
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (ptr->type == OBJ_SHIP)
return 1;
ptr = GET_NEXT(ptr);
}
return 0;
case OPF_WING:
for (j=0; j<MAX_WINGS; j++)
if (Wings[j].wave_count)
return 1;
return 0;
case OPF_PERSONA:
return (Num_personas > 0) ? 1 : 0;
case OPF_POINT:
case OPF_WAYPOINT_PATH:
return Waypoint_lists.empty() ? 0 : 1;
case OPF_MISSION_NAME:
if (m_mode != MODE_CAMPAIGN) {
if (!(*Mission_filename))
return 0;
return 1;
}
if (Campaign.num_missions > 0)
return 1;
return 0;
case OPF_GOAL_NAME: {
int value;
value = Operators[op].value;
if (m_mode == MODE_CAMPAIGN)
return 1;
// need to be sure that previous-goal functions are available. (i.e. we are providing a default argument for them)
else if ((value == OP_PREVIOUS_GOAL_TRUE) || (value == OP_PREVIOUS_GOAL_FALSE) || (value == OP_PREVIOUS_GOAL_INCOMPLETE) || Num_goals)
return 1;
return 0;
}
case OPF_EVENT_NAME: {
int value;
value = Operators[op].value;
if (m_mode == MODE_CAMPAIGN)
return 1;
// need to be sure that previous-event functions are available. (i.e. we are providing a default argument for them)
else if ((value == OP_PREVIOUS_EVENT_TRUE) || (value == OP_PREVIOUS_EVENT_FALSE) || (value == OP_PREVIOUS_EVENT_INCOMPLETE) || Num_mission_events)
return 1;
return 0;
}
case OPF_MESSAGE:
if (m_mode == MODE_EVENTS) {
Assert(Event_editor_dlg);
if (Event_editor_dlg->current_message_name(0))
return 1;
} else {
if (Num_messages > Num_builtin_messages)
return 1;
}
return 0;
case OPF_VARIABLE_NAME:
return (sexp_variable_count() > 0) ? 1 : 0;
case OPF_SSM_CLASS:
return (Ssm_info.size() > 0) ? 1 : 0;
case OPF_MISSION_MOOD:
return Builtin_moods.empty() ? 0 : 1;
default:
Int3();
}
return 0;
}
// expand a combined line (one with an operator and its one argument on the same line) into
// 2 lines.
void sexp_tree::expand_operator(int node)
{
int data;
HTREEITEM h;
if (tree_nodes[node].flags & COMBINED) {
node = tree_nodes[node].parent;
Assert((tree_nodes[node].flags & OPERAND) && (tree_nodes[node].flags & EDITABLE));
}
if ((tree_nodes[node].flags & OPERAND) && (tree_nodes[node].flags & EDITABLE)) { // expandable?
Assert(tree_nodes[node].type & SEXPT_OPERATOR);
h = tree_nodes[node].handle;
data = tree_nodes[node].child;
Assert(data != -1 && tree_nodes[data].next == -1 && tree_nodes[data].child == -1);
SetItem(h, TVIF_TEXT, tree_nodes[node].text, 0, 0, 0, 0, 0);
tree_nodes[node].flags = OPERAND;
int bmap = get_data_image(data);
tree_nodes[data].handle = insert(tree_nodes[data].text, bmap, bmap, h);
tree_nodes[data].flags = EDITABLE;
Expand(h, TVE_EXPAND);
}
}
// expand a CTreeCtrl branch and all of its children
void sexp_tree::expand_branch(HTREEITEM h)
{
Expand(h, TVE_EXPAND);
h = GetChildItem(h);
while (h) {
expand_branch(h);
h = GetNextSiblingItem(h);
}
}
void sexp_tree::merge_operator(int node)
{
/* 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;
}
}*/
}
// add a data node under operator pointed to by item_index
int sexp_tree::add_data(const char *data, int type)
{
int node;
expand_operator(item_index);
node = allocate_node(item_index);
set_node(node, type, data);
int bmap = get_data_image(node);
tree_nodes[node].handle = insert(data, bmap, bmap, tree_nodes[item_index].handle);
tree_nodes[node].flags = EDITABLE;
*modified = 1;
return node;
}
// add a (variable) data node under operator pointed to by item_index
int sexp_tree::add_variable_data(const char *data, int type)
{
int node;
Assert(type & SEXPT_VARIABLE);
expand_operator(item_index);
node = allocate_node(item_index);
set_node(node, type, data);
tree_nodes[node].handle = insert(data, BITMAP_VARIABLE, BITMAP_VARIABLE, tree_nodes[item_index].handle);
tree_nodes[node].flags = NOT_EDITABLE;
*modified = 1;
return node;
}
// add an operator under operator pointed to by item_index. Updates item_index to point
// to this new operator.
void sexp_tree::add_operator(const char *op, HTREEITEM h)
{
int node;
if (item_index == -1) {
node = allocate_node(-1);
set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
item_handle = tree_nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, h);
} else {
expand_operator(item_index);
node = allocate_node(item_index);
set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
item_handle = tree_nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, tree_nodes[item_index].handle);
}
tree_nodes[node].flags = OPERAND;
item_index = node;
*modified = 1;
}
// add an operator with one argument under operator pointed to by item_index. This function
// exists because the one arg case is a special case. The operator and argument is
// displayed on the same line.
/*void sexp_tree::add_one_arg_operator(char *op, char *data, int type)
{
char str[80];
int node1, node2;
expand_operator(item_index);
node1 = allocate_node(item_index);
node2 = allocate_node(node1);
set_node(node1, SEXPT_OPERATOR, op);
set_node(node2, type, data);
sprintf(str, "%s %s", op, data);
tree_nodes[node1].handle = insert(str, tree_nodes[item_index].handle);
tree_nodes[node1].flags = OPERAND | EDITABLE;
tree_nodes[node2].flags = COMBINED;
*modified = 1;
}*/
/*
int sexp_tree::verify_tree(int *bypass)
{
return verify_tree(0, bypass);
}
// check the sexp tree for errors. Return -1 if error, or 0 if no errors. If an error
// is found, item_index = node of error.
int sexp_tree::verify_tree(int node, int *bypass)
{
int i, type, count, op, type2, op2, argnum = 0;
if (!total_nodes)
return 0; // nothing to check
Assert(node >= 0 && node < tree_nodes.size());
Assert(tree_nodes[node].type == SEXPT_OPERATOR);
op = get_operator_index(tree_nodes[node].text);
if (op == -1)
return node_error(node, "Unknown operator", bypass);
count = count_args(tree_nodes[node].child);
if (count < Operators[op].min)
return node_error(node, "Too few arguments for operator", bypass);
if (count > Operators[op].max)
return node_error(node, "Too many arguments for operator", bypass);
node = tree_nodes[node].child; // get first argument
while (node != -1) {
type = query_operator_argument_type(op, argnum);
Assert(tree_nodes[node].type & SEXPT_VALID);
if (tree_nodes[node].type == SEXPT_OPERATOR) {
if (verify_tree(node) == -1)
return -1;
op2 = get_operator_index(tree_nodes[node].text); // no error checking, because it was done in the call above.
type2 = query_operator_return_type(op2);
} else if (tree_nodes[node].type == SEXPT_NUMBER) {
char *ptr;
type2 = OPR_NUMBER;
ptr = tree_nodes[node].text;
while (*ptr)
if (!isdigit(*ptr++))
return node_error(node, "Number is invalid", bypass);
} else if (tree_nodes[node].type == SEXPT_STRING) {
type2 = SEXP_ATOM_STRING;
} else
Assert(0); // unknown and invalid sexp node type.
switch (type) {
case OPF_NUMBER:
if (type2 != OPR_NUMBER)
return node_error(node, "Number or number return type expected here", bypass);
break;
case OPF_SHIP:
if (type2 == SEXP_ATOM_STRING)
if (ship_name_lookup(tree_nodes[node].text, 1) == -1)
type2 = 0;
if (type2 != SEXP_ATOM_STRING)
return node_error(node, "Ship name expected here", bypass);
break;
case OPF_WING:
if (type2 == SEXP_ATOM_STRING)
if (wing_name_lookup(tree_nodes[node].text) == -1)
type2 = 0;
if (type2 != SEXP_ATOM_STRING)
return node_error(node, "Wing name expected here", bypass);
break;
case OPF_SHIP_WING:
if (type2 == SEXP_ATOM_STRING)
if (ship_name_lookup(tree_nodes[node].text, 1) == -1)
if (wing_name_lookup(tree_nodes[node].text) == -1)
type2 = 0;
if (type2 != SEXP_ATOM_STRING)
return node_error(node, "Ship or wing name expected here", bypass);
break;
case OPF_BOOL:
if (type2 != OPR_BOOL)
return node_error(node, "Boolean return type expected here", bypass);
break;
case OPF_NULL:
if (type2 != OPR_NULL)
return node_error(node, "No return type operator expected here", bypass);
break;
case OPF_POINT:
if (type2 != SEXP_ATOM_STRING || verify_vector(tree_nodes[node].text))
return node_error(node, "3d coordinate expected here", bypass);
break;
case OPF_SUBSYSTEM:
case OPF_AWACS_SUBSYSTEM:
case OPF_ROTATING_SUBSYSTEM:
if (type2 == SEXP_ATOM_STRING)
if (ai_get_subsystem_type(tree_nodes[node].text) == SUBSYSTEM_UNKNOWN)
type2 = 0;
if (type2 != SEXP_ATOM_STRING)
return node_error(node, "Subsystem name expected here", bypass);
break;
case OPF_IFF:
if (type2 == SEXP_ATOM_STRING) {
for (i=0; i<Num_iffs; i++)
if (!stricmp(Team_names[i], tree_nodes[node].text))
break;
}
if (i == Num_iffs)
return node_error(node, "Iff team type expected here", bypass);
break;
case OPF_AI_GOAL:
if (type2 != OPR_AI_GOAL)
return node_error(node, "Ai goal return type expected here", bypass);
break;
case OPF_FLEXIBLE_ARGUMENT:
if (type2 != OPR_FLEXIBLE_ARGUMENT)
return node_error(node, "Flexible argument return type expected here", bypass);
break;
case OPF_ANYTHING:
break;
case OPF_DOCKER_POINT:
if (type2 != SEXP_ATOM_STRING)
return node_error(node, "Docker docking point name expected here", bypass);
break;
case OPF_DOCKEE_POINT:
if (type2 != SEXP_ATOM_STRING)
return node_error(node, "Dockee docking point name expected here", bypass);
break;
}
node = tree_nodes[node].next;
argnum++;
}
return 0;
}
*/
// display an error message and position to point of error (a node)
int sexp_tree::node_error(int node, const char *msg, int *bypass)
{
char text[512];
if (bypass)
*bypass = 1;
item_index = node;
item_handle = tree_nodes[node].handle;
if (tree_nodes[node].flags & COMBINED)
item_handle = tree_nodes[tree_nodes[node].parent].handle;
ensure_visible(node);
SelectItem(item_handle);
sprintf(text, "%s\n\nContinue checking for more errors?", msg);
if (MessageBox(text, "Sexp error", MB_YESNO | MB_ICONEXCLAMATION) == IDNO)
return -1;
else
return 0;
}
void sexp_tree::hilite_item(int node)
{
ensure_visible(node);
SelectItem(tree_nodes[node].handle);
}
// because the MFC function EnsureVisible() doesn't do what it says it does, I wrote this.
void sexp_tree::ensure_visible(int node)
{
Assert(node != -1);
if (tree_nodes[node].parent != -1)
ensure_visible(tree_nodes[node].parent); // expand all parents first
if (tree_nodes[node].child != -1) // expandable?
Expand(tree_nodes[node].handle, TVE_EXPAND); // expand this item
}
void sexp_tree::link_modified(int *ptr)
{
modified = ptr;
}
void get_variable_default_text_from_variable_text(char *text, char *default_text)
{
int len;
char *start;
// find '('
start = strstr(text, "(");
Assert(start);
start++;
// get length and copy all but last char ")"
len = strlen(start);
strncpy(default_text, start, len-1);
// add null termination
default_text[len-1] = '\0';
}
void get_variable_name_from_sexp_tree_node_text(const char *text, char *var_name)
{
int length;
length = strcspn(text, "(");
strncpy(var_name, text, length);
var_name[length] = '\0';
}
int sexp_tree::get_modify_variable_type(int parent)
{
int sexp_var_index = -1;
Assert(parent >= 0);
int op_const = get_operator_const(tree_nodes[parent].text);
Assert(tree_nodes[parent].child >= 0);
char *node_text = tree_nodes[tree_nodes[parent].child].text;
if ( op_const == OP_MODIFY_VARIABLE ) {
sexp_var_index = get_tree_name_to_sexp_variable_index(node_text);
Assert(sexp_var_index >= 0);
}
else if ( op_const == OP_SET_VARIABLE_BY_INDEX ) {
if (can_construe_as_integer(node_text)) {
sexp_var_index = atoi(node_text);
Assert(sexp_var_index >= 0);
}
else if (strchr(node_text, '(') && strchr(node_text, ')')) {
// the variable index is itself a variable!
return OPF_AMBIGUOUS;
}
} else {
Int3(); // should not be called otherwise
}
if (sexp_var_index < 0 || Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_BLOCK || Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NOT_USED) {
// assume number so that we can allow tree display of number operators
return OPF_NUMBER;
} else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
return OPF_NUMBER;
} else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
return OPF_AMBIGUOUS;
} else {
Int3();
return 0;
}
}
void sexp_tree::verify_and_fix_arguments(int node)
{
int op, arg_num, type, tmp;
static int flag = 0;
sexp_list_item *list, *ptr;
bool is_variable_arg = false;
if (flag)
return;
flag++;
op = get_operator_index(tree_nodes[node].text);
if (op < 0)
return;
tmp = item_index;
arg_num = 0;
item_index = tree_nodes[node].child;
while (item_index >= 0) {
// get listing of valid argument values for node item_index
type = query_operator_argument_type(op, arg_num);
// special case for modify-variable
if (type == OPF_AMBIGUOUS) {
is_variable_arg = true;
type = get_modify_variable_type(node);
}
if (query_restricted_opf_range(type)) {
list = get_listing_opf(type, node, arg_num);
if (!list && (arg_num >= Operators[op].min)) {
free_node(item_index, 1);
item_index = tmp;
flag--;
return;
}
if (list) {
// get a pointer to tree_nodes[item_index].text for normal value
// or default variable value if variable
char *text_ptr;
char default_variable_text[TOKEN_LENGTH];
if (tree_nodes[item_index].type & SEXPT_VARIABLE) {
// special case for SEXPs which can modify a variable
if ((arg_num == 0 && Operators[op].value == OP_MODIFY_VARIABLE) ||
(arg_num == 8 && Operators[op].value == OP_ADD_BACKGROUND_BITMAP) ||
(arg_num == 5 && Operators[op].value == OP_ADD_SUN_BITMAP) ||
(arg_num == 2 && Operators[op].value == OP_STRING_CONCATENATE) ||
(arg_num == 1 && Operators[op].value == OP_INT_TO_STRING) ||
(arg_num == 3 && Operators[op].value == OP_STRING_GET_SUBSTRING) ||
(arg_num == 4 && Operators[op].value == OP_STRING_SET_SUBSTRING) ||
(arg_num == 1 && Operators[op].value == OP_COPY_VARIABLE_FROM_INDEX))
{
// make text_ptr to start - before '('
get_variable_name_from_sexp_tree_node_text(tree_nodes[item_index].text, default_variable_text);
text_ptr = default_variable_text;
} else {
// only the type needs checking for variables. It's up the to the FREDder to ensure the value is valid
get_variable_name_from_sexp_tree_node_text(tree_nodes[item_index].text, default_variable_text);
int sexp_var_index = get_index_sexp_variable_name(default_variable_text);
bool types_match = false;
Assert(sexp_var_index != -1);
switch (type) {
case OPF_NUMBER:
case OPF_POSITIVE:
if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
types_match = true;
}
break;
default:
if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
types_match = true;
}
}
if (types_match) {
// on to the next argument
item_index = tree_nodes[item_index].next;
arg_num++;
continue;
}
else {
// shouldn't really be getting here unless someone has been hacking the mission in a text editor
get_variable_default_text_from_variable_text(tree_nodes[item_index].text, default_variable_text);
text_ptr = default_variable_text;
}
}
} else {
text_ptr = tree_nodes[item_index].text;
}
ptr = list;
while (ptr) {
if (ptr->text != NULL) {
// make sure text is not NULL
// check that proposed text is valid for operator
if ( !stricmp(ptr->text, text_ptr) )
break;
ptr = ptr->next;
} else {
// text is NULL, so set ptr to NULL to end loop
ptr = NULL;
}
}
if (!ptr) { // argument isn't in list of valid choices,
if (list->op >= 0) {
replace_operator(list->text);
} else {
replace_data(list->text, list->type);
}
}
} else {
bool invalid = false;
if (type == OPF_AMBIGUOUS) {
if (SEXPT_TYPE(tree_nodes[item_index].type) == SEXPT_OPERATOR) {
invalid = true;
}
} else {
if (SEXPT_TYPE(tree_nodes[item_index].type) != SEXPT_OPERATOR) {
invalid = true;
}
}
if (invalid) {
replace_data("<Invalid>", (SEXPT_STRING | SEXPT_VALID));
}
}
if (tree_nodes[item_index].type & SEXPT_OPERATOR)
verify_and_fix_arguments(item_index);
}
//fix the node if it is the argument for modify-variable
if (is_variable_arg //&&
// !(tree_nodes[item_index].type & SEXPT_OPERATOR || tree_nodes[item_index].type & SEXPT_VARIABLE )
) {
switch (type) {
case OPF_AMBIGUOUS:
tree_nodes[item_index].type |= SEXPT_STRING;
tree_nodes[item_index].type &= ~SEXPT_NUMBER;
break;
case OPF_NUMBER:
tree_nodes[item_index].type |= SEXPT_NUMBER;
tree_nodes[item_index].type &= ~SEXPT_STRING;
break;
default:
Int3();
}
}
item_index = tree_nodes[item_index].next;
arg_num++;
}
item_index = tmp;
flag--;
}
void sexp_tree::replace_data(const char *data, int type)
{
int node;
HTREEITEM h;
node = tree_nodes[item_index].child;
if (node != -1)
free_node2(node);
tree_nodes[item_index].child = -1;
h = tree_nodes[item_index].handle;
while (ItemHasChildren(h))
DeleteItem(GetChildItem(h));
set_node(item_index, type, data);
SetItemText(h, data);
int bmap = get_data_image(item_index);
SetItemImage(h, bmap, bmap);
tree_nodes[item_index].flags = EDITABLE;
// check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
verify_and_fix_arguments(tree_nodes[item_index].parent);
*modified = 1;
update_help(GetSelectedItem());
}
// Replaces data with sexp_variable type data
void sexp_tree::replace_variable_data(int var_idx, int type)
{
int node;
HTREEITEM h;
char buf[128];
Assert(type & SEXPT_VARIABLE);
node = tree_nodes[item_index].child;
if (node != -1)
free_node2(node);
tree_nodes[item_index].child = -1;
h = tree_nodes[item_index].handle;
while (ItemHasChildren(h)) {
DeleteItem(GetChildItem(h));
}
// Assemble name
sprintf(buf, "%s(%s)", Sexp_variables[var_idx].variable_name, Sexp_variables[var_idx].text);
set_node(item_index, type, buf);
SetItemText(h, buf);
SetItemImage(h, BITMAP_VARIABLE, BITMAP_VARIABLE);
tree_nodes[item_index].flags = NOT_EDITABLE;
// check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
verify_and_fix_arguments(tree_nodes[item_index].parent);
*modified = 1;
update_help(GetSelectedItem());
}
void sexp_tree::replace_operator(const char *op)
{
int node;
HTREEITEM h;
node = tree_nodes[item_index].child;
if (node != -1)
free_node2(node);
tree_nodes[item_index].child = -1;
h = tree_nodes[item_index].handle;
while (ItemHasChildren(h))
DeleteItem(GetChildItem(h));
set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), op);
SetItemText(h, op);
tree_nodes[item_index].flags = OPERAND;
*modified = 1;
update_help(GetSelectedItem());
// hack added at Allender's request. If changing ship in an ai-dock operator, re-default
// docking point.
}
/*void sexp_tree::replace_one_arg_operator(char *op, char *data, int type)
{
char str[80];
int node;
HTREEITEM h;
node = tree_nodes[item_index].child;
if (node != -1)
free_node2(node);
tree_nodes[item_index].child = -1;
h = tree_nodes[item_index].handle;
while (ItemHasChildren(h))
DeleteItem(GetChildItem(h));
node = allocate_node(item_index);
set_node(item_index, SEXPT_OPERATOR, op);
set_node(node, type, data);
sprintf(str, "%s %s", op, data);
SetItemText(h, str);
tree_nodes[item_index].flags = OPERAND | EDITABLE;
tree_nodes[node].flags = COMBINED;
*modified = 1;
update_help(GetSelectedItem());
}*/
// moves a whole sexp tree branch to a new position under 'parent' and after 'after'.
// The expansion state is preserved, and node handles are updated.
void sexp_tree::move_branch(int source, int parent)
{
int node;
// if no source, skip everything
if (source != -1) {
node = tree_nodes[source].parent;
if (node != -1) {
if (tree_nodes[node].child == source)
tree_nodes[node].child = tree_nodes[source].next;
else {
node = tree_nodes[node].child;
while (tree_nodes[node].next != source) {
node = tree_nodes[node].next;
Assert(node != -1);
}
tree_nodes[node].next = tree_nodes[source].next;
}
}
tree_nodes[source].parent = parent;
tree_nodes[source].next = -1;
if (parent) {
if (tree_nodes[parent].child == -1)
tree_nodes[parent].child = source;
else {
node = tree_nodes[parent].child;
while (tree_nodes[node].next != -1)
node = tree_nodes[node].next;
tree_nodes[node].next = source;
}
move_branch(tree_nodes[source].handle, tree_nodes[parent].handle);
} else
move_branch(tree_nodes[source].handle);
}
}
HTREEITEM sexp_tree::move_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after)
{
uint i;
int image1, image2;
HTREEITEM h = 0, child, next;
if (source) {
for (i=0; i<tree_nodes.size(); i++)
if (tree_nodes[i].handle == source)
break;
if (i < tree_nodes.size()) {
GetItemImage(source, image1, image2);
h = insert(GetItemText(source), image1, image2, parent, after);
tree_nodes[i].handle = h;
} else {
GetItemImage(source, image1, image2);
h = insert(GetItemText(source), image1, image2, parent, after);
}
SetItemData(h, GetItemData(source));
child = GetChildItem(source);
while (child) {
next = GetNextSiblingItem(child);
move_branch(child, h);
child = next;
}
if (GetItemState(source, TVIS_EXPANDED) & TVIS_EXPANDED)
Expand(h, TVE_EXPAND);
DeleteItem(source);
}
return h;
}
void sexp_tree::copy_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after)
{
uint i;
int image1, image2;
HTREEITEM h, child;
if (source) {
for (i=0; i<tree_nodes.size(); i++)
if (tree_nodes[i].handle == source)
break;
if (i < tree_nodes.size()) {
GetItemImage(source, image1, image2);
h = insert(GetItemText(source), image1, image2, parent, after);
tree_nodes[i].handle = h;
} else {
GetItemImage(source, image1, image2);
h = insert(GetItemText(source), image1, image2, parent, after);
}
SetItemData(h, GetItemData(source));
child = GetChildItem(source);
while (child) {
copy_branch(child, h);
child = GetNextSiblingItem(child);
}
if (GetItemState(source, TVIS_EXPANDED) & TVIS_EXPANDED)
Expand(h, TVE_EXPAND);
}
}
void sexp_tree::swap_roots(HTREEITEM one, HTREEITEM two)
{
HTREEITEM h;
Assert(!GetParentItem(one));
Assert(!GetParentItem(two));
// copy_branch(one, TVI_ROOT, two);
// move_branch(two, TVI_ROOT, one);
// DeleteItem(one);
h = move_branch(one, TVI_ROOT, two);
SelectItem(h);
SelectItem(h);
*modified = 1;
}
void sexp_tree::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
{
UINT flags = 0;
// ScreenToClient(&m_pt);
ASSERT(!m_dragging);
m_h_drag = HitTest(m_pt, &flags);
m_h_drop = NULL;
if (!m_mode || GetParentItem(m_h_drag))
return;
ASSERT(m_p_image_list == NULL);
m_p_image_list = CreateDragImage(m_h_drag); // get the image list for dragging
if (!m_p_image_list)
return;
m_p_image_list->DragShowNolock(TRUE);
m_p_image_list->SetDragCursorImage(0, CPoint(0, 0));
m_p_image_list->BeginDrag(0, CPoint(0,0));
m_p_image_list->DragMove(m_pt);
m_p_image_list->DragEnter(this, m_pt);
SetCapture();
m_dragging = TRUE;
}
void sexp_tree::OnLButtonDown(UINT nFlags, CPoint point)
{
m_pt = point;
CTreeCtrl::OnLButtonDown(nFlags, point);
}
void sexp_tree::OnMouseMove(UINT nFlags, CPoint point)
{
HTREEITEM hitem = NULL;
UINT flags = 0;
if (m_dragging) {
ASSERT(m_p_image_list != NULL);
m_p_image_list->DragMove(point);
if ((hitem = HitTest(point, &flags)) != NULL)
if (!GetParentItem(hitem)) {
m_p_image_list->DragLeave(this);
SelectDropTarget(hitem);
m_h_drop = hitem;
m_p_image_list->DragEnter(this, point);
}
}
CTreeCtrl::OnMouseMove(nFlags, point);
}
void sexp_tree::OnLButtonUp(UINT nFlags, CPoint point)
{
int index1, index2;
if (m_dragging) {
ASSERT(m_p_image_list != NULL);
m_p_image_list->DragLeave(this);
m_p_image_list->EndDrag();
delete m_p_image_list;
m_p_image_list = NULL;
if (m_h_drop && m_h_drag != m_h_drop) {
Assert(m_h_drag);
index1 = GetItemData(m_h_drag);
index2 = GetItemData(m_h_drop);
swap_roots(m_h_drag, m_h_drop);
if (m_mode == MODE_GOALS) {
Assert(Goal_editor_dlg);
Goal_editor_dlg->swap_handler(index1, index2);
} else if (m_mode == MODE_EVENTS) {
Assert(Event_editor_dlg);
Event_editor_dlg->swap_handler(index1, index2);
} else if (m_mode == MODE_CAMPAIGN) {
Campaign_tree_formp->swap_handler(index1, index2);
} else
Assert(0);
} else
MessageBeep(0);
ReleaseCapture();
m_dragging = FALSE;
SelectDropTarget(NULL);
}
CTreeCtrl::OnLButtonUp(nFlags, point);
}
const static UINT Numbered_data_bitmaps[] = {
IDB_DATA_00,
IDB_DATA_05,
IDB_DATA_10,
IDB_DATA_15,
IDB_DATA_20,
IDB_DATA_25,
IDB_DATA_30,
IDB_DATA_35,
IDB_DATA_40,
IDB_DATA_45,
IDB_DATA_50,
IDB_DATA_55,
IDB_DATA_60,
IDB_DATA_65,
IDB_DATA_70,
IDB_DATA_75,
IDB_DATA_80,
IDB_DATA_85,
IDB_DATA_90,
IDB_DATA_95
};
void sexp_tree::setup(CEdit *ptr)
{
CImageList *pimagelist;
CBitmap bitmap;
help_box = ptr;
if (help_box) {
int stops[2] = { 10, 30 };
help_box -> SetTabStops(2, (LPINT) stops);
}
pimagelist = GetImageList(TVSIL_NORMAL);
if (!pimagelist) {
pimagelist = new CImageList();
pimagelist->Create(16, 16, TRUE/*bMask*/, 2, 22);
//*****Add generic images
bitmap.LoadBitmap(IDB_OPERATOR);
pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
bitmap.DeleteObject();
bitmap.LoadBitmap(IDB_DATA);
pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
bitmap.DeleteObject();
bitmap.LoadBitmap(IDB_VARIABLE);
pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
bitmap.DeleteObject();
bitmap.LoadBitmap(IDB_ROOT);
pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
bitmap.DeleteObject();
bitmap.LoadBitmap(IDB_ROOT_DIRECTIVE);
pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
bitmap.DeleteObject();
bitmap.LoadBitmap(IDB_CHAINED);
pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
bitmap.DeleteObject();
bitmap.LoadBitmap(IDB_CHAINED_DIRECTIVE);
pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
bitmap.DeleteObject();
bitmap.LoadBitmap(IDB_GREEN_DOT);
pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
bitmap.DeleteObject();
bitmap.LoadBitmap(IDB_BLACK_DOT);
pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
bitmap.DeleteObject();
//*****Add numbered data entries
int num = sizeof(Numbered_data_bitmaps)/sizeof(UINT);
int i = 0;
for(i = 0; i < num; i++)
{
bitmap.LoadBitmap(Numbered_data_bitmaps[i]);
pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
bitmap.DeleteObject();
}
SetImageList(pimagelist, TVSIL_NORMAL);
}
}
//#define BITMAP_OPERATOR 0
//#define BITMAP_DATA 1
//#define BITMAP_VARIABLE 2
//#define BITMAP_ROOT 3
//#define BITMAP_ROOT_DIRECTIVE 4
//#define BITMAP_CHAIN 5
//#define BITMAP_CHAIN_DIRECTIVE 6
//#define BITMAP_GREEN_DOT 7
//#define BITMAP_BLACK_DOT 8
HTREEITEM sexp_tree::insert(LPCTSTR lpszItem, int image, int sel_image, HTREEITEM hParent, HTREEITEM hInsertAfter)
{
return InsertItem(lpszItem, image, sel_image, hParent, hInsertAfter);
}
void sexp_tree::OnDestroy()
{
CImageList *pimagelist;
pimagelist = GetImageList(TVSIL_NORMAL);
if (pimagelist) {
pimagelist->DeleteImageList();
delete pimagelist;
}
CTreeCtrl::OnDestroy();
}
HTREEITEM sexp_tree::handle(int node)
{
return tree_nodes[node].handle;
}
const char *sexp_tree::help(int code)
{
int i;
i = Num_sexp_help;
while (i--) {
if (Sexp_help[i].id == code)
break;
}
if (i >= 0)
return Sexp_help[i].help;
return NULL;
}
// get type of item clicked on
int sexp_tree::get_type(HTREEITEM h)
{
uint i;
// get index into sexp_tree
for (i=0; i<tree_nodes.size(); i++)
if (tree_nodes[i].handle == h)
break;
if ( (i >= tree_nodes.size()) ) {
// Int3(); // This would be the root of the tree -- ie, event name
return -1;
}
return tree_nodes[i].type;
}
void sexp_tree::update_help(HTREEITEM h)
{
const char *str;
int i, j, z, c, code, index, sibling_place;
CString text;
for (i=0; i<Num_operators; i++) {
for (j=0; j<Num_op_menus; j++) {
if (get_category(Operators[i].value) == op_menu[j].id) {
if (!help(Operators[i].value)) {
mprintf(("Allender! If you add new sexp operators, add help for them too! :)\n"));
}
}
}
}
help_box = (CEdit *) GetParent()->GetDlgItem(IDC_HELP_BOX);
if (!help_box || !::IsWindow(help_box->m_hWnd))
return;
mini_help_box = (CEdit *) GetParent()->GetDlgItem(IDC_MINI_HELP_BOX);
if (!mini_help_box || !::IsWindow(mini_help_box->m_hWnd))
return;
for (i=0; i<(int)tree_nodes.size(); i++)
if (tree_nodes[i].handle == h)
break;
if ((i >= (int)tree_nodes.size()) || !tree_nodes[i].type) {
help_box->SetWindowText("");
mini_help_box->SetWindowText("");
return;
}
if (SEXPT_TYPE(tree_nodes[i].type) == SEXPT_OPERATOR)
{
mini_help_box->SetWindowText("");
}
else
{
z = tree_nodes[i].parent;
if (z < 0) {
Warning(LOCATION, "Sexp data \"%s\" has no parent!", tree_nodes[i].text);
return;
}
code = get_operator_const(tree_nodes[z].text);
index = get_operator_index(tree_nodes[z].text);
sibling_place = get_sibling_place(i) + 1; //We want it to start at 1
//*****Minihelp box
if((SEXPT_TYPE(tree_nodes[i].type) == SEXPT_NUMBER) || (SEXPT_TYPE(tree_nodes[i].type) == SEXPT_STRING) && sibling_place > 0)
{
char buffer[10240] = {""};
//Get the help for the current operator
const char *helpstr = help(code);
bool display_number = true;
//If a help string exists, try to display it
if(helpstr != NULL)
{
char searchstr[32];
const char *loc=NULL, *loc2=NULL;
if(loc == NULL)
{
sprintf(searchstr, "\n%d:", sibling_place);
loc = strstr(helpstr, searchstr);
}
if(loc == NULL)
{
sprintf(searchstr, "\t%d:", sibling_place);
loc = strstr(helpstr, searchstr);
}
if(loc == NULL)
{
sprintf(searchstr, " %d:", sibling_place);
loc = strstr(helpstr, searchstr);
}
if(loc == NULL)
{
sprintf(searchstr, "%d:", sibling_place);
loc = strstr(helpstr, searchstr);
}
if(loc == NULL)
{
loc = strstr(helpstr, "Rest:");
}
if(loc == NULL)
{
loc = strstr(helpstr, "All:");
}
if(loc != NULL)
{
//Skip whitespace
while(*loc=='\r' || *loc == '\n' || *loc == ' ' || *loc == '\t') loc++;
//Find EOL
loc2 = strpbrk(loc, "\r\n");
if(loc2 != NULL)
{
size_t size = loc2-loc;
strncpy(buffer, loc, size);
if(size < sizeof(buffer))
{
buffer[size] = '\0';
}
display_number = false;
}
else
{
strcpy_s(buffer, loc);
display_number = false;
}
}
}
//Display argument number
if(display_number)
{
sprintf(buffer, "%d:", sibling_place);
}
mini_help_box->SetWindowText(buffer);
}
if (index >= 0) {
c = 0;
j = tree_nodes[z].child;
while ((j >= 0) && (j != i)) {
j = tree_nodes[j].next;
c++;
}
Assert(j >= 0);
if (query_operator_argument_type(index, c) == OPF_MESSAGE) {
for (j=0; j<Num_messages; j++)
if (!stricmp(Messages[j].name, tree_nodes[i].text)) {
text.Format("Message Text:\r\n%s", Messages[j].message);
help_box->SetWindowText(text);
return;
}
}
}
i = z;
}
code = get_operator_const(tree_nodes[i].text);
str = help(code);
if (!str)
str = "No help available";
help_box->SetWindowText(str);
}
// find list of sexp_tree nodes with text
// stuff node indices into find[]
int sexp_tree::find_text(const char *text, int *find)
{
uint i;
int find_count;
// initialize find
for (i=0; i<MAX_SEARCH_MESSAGE_DEPTH; i++) {
find[i] = -1;
}
find_count = 0;
for (i=0; i<tree_nodes.size(); i++) {
// only look at used and editable nodes
if ((tree_nodes[i].flags & EDITABLE && (tree_nodes[i].type != SEXPT_UNUSED))) {
// find the text
if ( !stricmp(tree_nodes[i].text, text) ) {
find[find_count++] = i;
// don't exceed max count - array bounds
if (find_count == MAX_SEARCH_MESSAGE_DEPTH) {
break;
}
}
}
}
return find_count;
}
void sexp_tree::OnKeydown(NMHDR *pNMHDR, LRESULT *pResult)
{
int key;
TV_KEYDOWN *pTVKeyDown = (TV_KEYDOWN *) pNMHDR;
key = pTVKeyDown->wVKey;
if (key == VK_SPACE)
EditLabel(GetSelectedItem());
*pResult = 0;
}
// Determine if a given opf code has a restricted argument range (i.e. has a specific, limited
// set of argument values, or has virtually unlimited possibilities. For example, boolean values
// only have true or false, so it is restricted, but a number could be anything, so it's not.
//
int sexp_tree::query_restricted_opf_range(int opf)
{
switch (opf) {
case OPF_NUMBER:
case OPF_POSITIVE:
case OPF_WHO_FROM:
// Goober5000 - these are needed too (otherwise the arguments revert to their defaults)
case OPF_STRING:
case OPF_ANYTHING:
return 0;
}
return 1;
}
// generate listing of valid argument values.
// opf = operator format to generate list for
// parent_node = the parent node we are generating list for
// arg_index = argument number of parent this argument will go at
//
// Goober5000 - add the listing from get_listing_opf_sub to the end of a new list containing
// the special argument item, but only if it's a child of a when-argument (or similar) sexp.
// Also only do this if the list has at least one item, because otherwise the argument code
// would have nothing to select from.
sexp_list_item *sexp_tree::get_listing_opf(int opf, int parent_node, int arg_index)
{
sexp_list_item head;
sexp_list_item *list = NULL;
int w_arg, e_arg;
switch (opf) {
case OPF_NONE:
list = NULL;
break;
case OPF_NULL:
list = get_listing_opf_null();
break;
case OPF_BOOL:
list = get_listing_opf_bool(parent_node);
break;
case OPF_NUMBER:
list = get_listing_opf_number();
break;
case OPF_SHIP:
list = get_listing_opf_ship(parent_node);
break;
case OPF_WING:
list = get_listing_opf_wing();
break;
case OPF_AWACS_SUBSYSTEM:
case OPF_ROTATING_SUBSYSTEM:
case OPF_SUBSYSTEM:
list = get_listing_opf_subsystem(parent_node, arg_index);
break;
case OPF_SUBSYSTEM_TYPE:
list = get_listing_opf_subsystem_type(parent_node);
break;
case OPF_POINT:
list = get_listing_opf_point();
break;
case OPF_IFF:
list = get_listing_opf_iff();
break;
case OPF_AI_CLASS:
list = get_listing_opf_ai_class();
break;
case OPF_SUPPORT_SHIP_CLASS:
list = get_listing_opf_support_ship_class();
break;
case OPF_SSM_CLASS:
list = get_listing_opf_ssm_class();
break;
case OPF_ARRIVAL_LOCATION:
list = get_listing_opf_arrival_location();
break;
case OPF_DEPARTURE_LOCATION:
list = get_listing_opf_departure_location();
break;
case OPF_ARRIVAL_ANCHOR_ALL:
list = get_listing_opf_arrival_anchor_all();
break;
case OPF_SHIP_WITH_BAY:
list = get_listing_opf_ship_with_bay();
break;
case OPF_SOUNDTRACK_NAME:
list = get_listing_opf_soundtrack_name();
break;
case OPF_AI_GOAL:
list = get_listing_opf_ai_goal(parent_node);
break;
case OPF_FLEXIBLE_ARGUMENT:
list = get_listing_opf_flexible_argument();
break;
case OPF_DOCKER_POINT:
list = get_listing_opf_docker_point(parent_node);
break;
case OPF_DOCKEE_POINT:
list = get_listing_opf_dockee_point(parent_node);
break;
case OPF_MESSAGE:
list = get_listing_opf_message();
break;
case OPF_WHO_FROM:
list = get_listing_opf_who_from();
break;
case OPF_PRIORITY:
list = get_listing_opf_priority();
break;
case OPF_WAYPOINT_PATH:
list = get_listing_opf_waypoint_path();
break;
case OPF_POSITIVE:
list = get_listing_opf_positive();
break;
case OPF_MISSION_NAME:
list = get_listing_opf_mission_name();
break;
case OPF_SHIP_POINT:
list = get_listing_opf_ship_point();
break;
case OPF_GOAL_NAME:
list = get_listing_opf_goal_name(parent_node);
break;
case OPF_SHIP_WING:
list = get_listing_opf_ship_wing();
break;
case OPF_SHIP_WING_WHOLETEAM:
list = get_listing_opf_ship_wing_wholeteam();
break;
case OPF_SHIP_WING_SHIPONTEAM_POINT:
list = get_listing_opf_ship_wing_shiponteam_point();
break;
case OPF_SHIP_WING_POINT:
list = get_listing_opf_ship_wing_point();
break;
case OPF_SHIP_WING_POINT_OR_NONE:
list = get_listing_opf_ship_wing_point_or_none();
break;
case OPF_ORDER_RECIPIENT:
list = get_listing_opf_order_recipient();
break;
case OPF_SHIP_TYPE:
list = get_listing_opf_ship_type();
break;
case OPF_KEYPRESS:
list = get_listing_opf_keypress();
break;
case OPF_EVENT_NAME:
list = get_listing_opf_event_name(parent_node);
break;
case OPF_AI_ORDER:
list = get_listing_opf_ai_order();
break;
case OPF_SKILL_LEVEL:
list = get_listing_opf_skill_level();
break;
case OPF_CARGO:
list = get_listing_opf_cargo();
break;
case OPF_STRING:
list = get_listing_opf_string();
break;
case OPF_MEDAL_NAME:
list = get_listing_opf_medal_name();
break;
case OPF_WEAPON_NAME:
list = get_listing_opf_weapon_name();
break;
case OPF_INTEL_NAME:
list = get_listing_opf_intel_name();
break;
case OPF_SHIP_CLASS_NAME:
list = get_listing_opf_ship_class_name();
break;
case OPF_HUD_GAUGE_NAME:
list = get_listing_opf_hud_gauge_name();
break;
case OPF_HUGE_WEAPON:
list = get_listing_opf_huge_weapon();
break;
case OPF_SHIP_NOT_PLAYER:
list = get_listing_opf_ship_not_player();
break;
case OPF_SHIP_OR_NONE:
list = get_listing_opf_ship_or_none();
break;
case OPF_SUBSYSTEM_OR_NONE:
list = get_listing_opf_subsystem_or_none(parent_node, arg_index);
break;
case OPF_SUBSYS_OR_GENERIC:
list = get_listing_opf_subsys_or_generic(parent_node, arg_index);
break;
case OPF_JUMP_NODE_NAME:
list = get_listing_opf_jump_nodes();
break;
case OPF_VARIABLE_NAME:
list = get_listing_opf_variable_names();
break;
case OPF_AMBIGUOUS:
list = NULL;
break;
case OPF_ANYTHING:
list = NULL;
break;
case OPF_SKYBOX_MODEL_NAME:
list = get_listing_opf_skybox_model();
break;
case OPF_SKYBOX_FLAGS:
list = get_listing_opf_skybox_flags();
break;
case OPF_BACKGROUND_BITMAP:
list = get_listing_opf_background_bitmap();
break;
case OPF_SUN_BITMAP:
list = get_listing_opf_sun_bitmap();
break;
case OPF_NEBULA_STORM_TYPE:
list = get_listing_opf_nebula_storm_type();
break;
case OPF_NEBULA_POOF:
list = get_listing_opf_nebula_poof();
break;
case OPF_TURRET_TARGET_ORDER:
list = get_listing_opf_turret_target_order();
break;
case OPF_TARGET_PRIORITIES:
list = get_listing_opf_turret_target_priorities();
break;
case OPF_ARMOR_TYPE:
list = get_listing_opf_armor_type();
break;
case OPF_DAMAGE_TYPE:
list = get_listing_opf_damage_type();
break;
case OPF_ANIMATION_TYPE:
list = get_listing_opf_animation_type();
break;
case OPF_PERSONA:
list = get_listing_opf_persona();
break;
case OPF_POST_EFFECT:
list = get_listing_opf_post_effect();
break;
case OPF_FONT:
list = get_listing_opf_font();
break;
case OPF_HUD_ELEMENT:
list = get_listing_opf_hud_elements();
break;
case OPF_SOUND_ENVIRONMENT:
list = get_listing_opf_sound_environment();
break;
case OPF_SOUND_ENVIRONMENT_OPTION:
list = get_listing_opf_sound_environment_option();
break;
case OPF_AUDIO_VOLUME_OPTION:
list = get_listing_opf_adjust_audio_volume();
break;
case OPF_EXPLOSION_OPTION:
list = get_listing_opf_explosion_option();
break;
case OPF_WEAPON_BANK_NUMBER:
list = get_listing_opf_weapon_banks();
break;
case OPF_MESSAGE_OR_STRING:
list = get_listing_opf_message();
break;
case OPF_HUD_GAUGE:
list = get_listing_opf_hud_gauge();
break;
case OPF_SHIP_EFFECT:
list = get_listing_opf_ship_effect();
break;
case OPF_MISSION_MOOD:
list = get_listing_opf_mission_moods();
break;
case OPF_SHIP_FLAG:
list = get_listing_opf_ship_flags();
break;
case OPF_TEAM_COLOR:
list = get_listing_opf_team_colors();
break;
case OPF_NEBULA_PATTERN:
list = get_listing_opf_nebula_patterns();
break;
case OPF_GAME_SND:
list = get_listing_opf_game_snds();
break;
default:
Int3(); // unknown OPF code
list = NULL;
break;
}
// skip OPF_NONE, also skip for OPF_NULL, because it takes no data (though it can take plenty of operators)
if (opf == OPF_NULL || opf == OPF_NONE) {
return list;
}
// skip the special argument if we aren't at the right spot in when-argument or
// every-time-argument... meaning if either w_arg or e_arg is >= 1, we can continue
w_arg = find_ancestral_argument_number(OP_WHEN_ARGUMENT, parent_node);
e_arg = find_ancestral_argument_number(OP_EVERY_TIME_ARGUMENT, parent_node);
if (w_arg < 1 && e_arg < 1 /* && the same for any future _ARGUMENT sexps */ ) {
return list;
}
// the special item is a string and should not be added for numeric lists
if (opf != OPF_NUMBER && opf != OPF_POSITIVE) {
head.add_data(SEXP_ARGUMENT_STRING);
}
if (list != NULL) {
// append other list
head.add_list(list);
}
// return listing
return head.next;
}
// Goober5000
int sexp_tree::find_argument_number(int parent_node, int child_node)
{
int arg_num, current_node;
// code moved/adapted from match_closest_operator
arg_num = 0;
current_node = tree_nodes[parent_node].child;
while (current_node >= 0)
{
// found?
if (current_node == child_node)
return arg_num;
// continue iterating
arg_num++;
current_node = tree_nodes[current_node].next;
}
// not found
return -1;
}
// Goober5000
// backtrack through parents until we find the operator matching
// parent_op, then find the argument we went through
int sexp_tree::find_ancestral_argument_number(int parent_op, int child_node)
{
if(child_node == -1)
return -1;
int parent_node;
int current_node;
current_node = child_node;
parent_node = tree_nodes[current_node].parent;
while (parent_node >= 0)
{
// check if the parent operator is the one we're looking for
if (get_operator_const(tree_nodes[parent_node].text) == parent_op)
return find_argument_number(parent_node, current_node);
// continue iterating up the tree
current_node = parent_node;
parent_node = tree_nodes[current_node].parent;
}
// not found
return -1;
}
/**
* Gets the proper data image for the tree item's place
* in its parent hierarchy.
*/
int sexp_tree::get_data_image(int node)
{
int count = get_sibling_place(node) + 1;
if (count <= 0) {
return BITMAP_DATA;
}
if (count % 5) {
return BITMAP_DATA;
}
int idx = (count % 100) / 5;
int num = sizeof(Numbered_data_bitmaps)/sizeof(UINT);
if (idx > num) {
return BITMAP_DATA;
}
return BITMAP_NUMBERED_DATA + idx;
}
int sexp_tree::get_sibling_place(int node)
{
if(tree_nodes[node].parent < 0 || tree_nodes[node].parent > (int)tree_nodes.size())
return -1;
sexp_tree_item *myparent = &tree_nodes[tree_nodes[node].parent];
if(myparent->child == -1)
return -1;
sexp_tree_item *mysibling = &tree_nodes[myparent->child];
int count = 0;
while(true)
{
if(mysibling == &tree_nodes[node])
break;
if(mysibling->next == -1)
break;
count++;
mysibling = &tree_nodes[mysibling->next];
}
return count;
}
sexp_list_item *sexp_tree::get_listing_opf_null()
{
int i;
sexp_list_item head;
for (i=0; i<Num_operators; i++)
if (query_operator_return_type(i) == OPR_NULL)
head.add_op(i);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_flexible_argument()
{
int i;
sexp_list_item head;
for (i=0; i<Num_operators; i++)
if (query_operator_return_type(i) == OPR_FLEXIBLE_ARGUMENT)
head.add_op(i);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_bool(int parent_node)
{
int i, only_basic;
sexp_list_item head;
// search for the previous goal/event operators. If found, only add the true/false
// sexpressions to the list
only_basic = 0;
if ( parent_node != -1 ) {
int op;
op = get_operator_const(tree_nodes[parent_node].text);
if ( (op == OP_PREVIOUS_GOAL_TRUE) || (op == OP_PREVIOUS_GOAL_FALSE) || (op == OP_PREVIOUS_EVENT_TRUE) || (op == OP_PREVIOUS_EVENT_FALSE) )
only_basic = 1;
}
for (i=0; i<Num_operators; i++) {
if (query_operator_return_type(i) == OPR_BOOL) {
if ( !only_basic || (only_basic && ((Operators[i].value == OP_TRUE) || (Operators[i].value == OP_FALSE))) ) {
head.add_op(i);
}
}
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_positive()
{
int i, z;
sexp_list_item head;
for (i=0; i<Num_operators; i++) {
z = query_operator_return_type(i);
// Goober5000's number hack
if ((z == OPR_NUMBER) || (z == OPR_POSITIVE))
head.add_op(i);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_number()
{
int i, z;
sexp_list_item head;
for (i=0; i<Num_operators; i++) {
z = query_operator_return_type(i);
if ((z == OPR_NUMBER) || (z == OPR_POSITIVE))
head.add_op(i);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship(int parent_node)
{
object *ptr;
sexp_list_item head;
int op = 0, dock_ship = -1, require_cap_ship = 0;
// look at the parent node and get the operator. Some ship lists should be filtered based
// on what the parent operator is
if ( parent_node >= 0 ) {
op = get_operator_const(tree_nodes[parent_node].text);
// get the dock_ship number of if this goal is an ai dock goal. used to prune out unwanted ships out
// of the generated ship list
dock_ship = -1;
if ( op == OP_AI_DOCK ) {
int z;
z = tree_nodes[parent_node].parent;
Assert(z >= 0);
Assert(!stricmp(tree_nodes[z].text, "add-ship-goal") || !stricmp(tree_nodes[z].text, "add-wing-goal") || !stricmp(tree_nodes[z].text, "add-goal"));
z = tree_nodes[z].child;
Assert(z >= 0);
dock_ship = ship_name_lookup(tree_nodes[z].text, 1);
Assert( dock_ship != -1 );
}
}
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
if ( op == OP_AI_DOCK ) {
// only include those ships in the list which the given ship can dock with.
if ( (dock_ship != ptr->instance) && ship_docking_valid(dock_ship , ptr->instance) )
head.add_data(Ships[ptr->instance].ship_name );
}
else if (op == OP_CAP_SUBSYS_CARGO_KNOWN_DELAY) {
if ( ((Ship_info[Ships[ptr->instance].ship_info_index].flags & SIF_HUGE_SHIP) && // big ship
!(Ships[ptr->instance].flags2 & SF2_TOGGLE_SUBSYSTEM_SCANNING) )|| // which is not flagged OR
((!(Ship_info[Ships[ptr->instance].ship_info_index].flags & SIF_HUGE_SHIP)) && // small ship
(Ships[ptr->instance].flags2 & SF2_TOGGLE_SUBSYSTEM_SCANNING) ) ) { // which is flagged
head.add_data(Ships[ptr->instance].ship_name);
}
}
else {
if ( !require_cap_ship || (Ship_info[Ships[ptr->instance].ship_info_index].flags & SIF_HUGE_SHIP) ) {
head.add_data(Ships[ptr->instance].ship_name);
}
}
}
ptr = GET_NEXT(ptr);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_wing()
{
int i;
sexp_list_item head;
for (i=0; i<MAX_WINGS; i++){
if (Wings[i].wave_count){
head.add_data(Wings[i].name);
}
}
return head.next;
}
// specific types of subsystems we're looking for
#define OPS_CAP_CARGO 1
#define OPS_STRENGTH 2
#define OPS_BEAM_TURRET 3
#define OPS_AWACS 4
#define OPS_ROTATE 5
#define OPS_ARMOR 6
sexp_list_item *sexp_tree::get_listing_opf_subsystem(int parent_node, int arg_index)
{
int op, child, sh;
int special_subsys = 0;
sexp_list_item head;
ship_subsys *subsys;
// determine if the parent is one of the set subsystem strength items. If so,
// we want to append the "Hull" name onto the end of the menu
Assert(parent_node >= 0);
// get the operator type of the node
op = get_operator_const(tree_nodes[parent_node].text);
// first child node
child = tree_nodes[parent_node].child;
Assert(child >= 0);
switch(op)
{
// where we care about hull strength
case OP_REPAIR_SUBSYSTEM:
case OP_SABOTAGE_SUBSYSTEM:
case OP_SET_SUBSYSTEM_STRNGTH:
special_subsys = OPS_STRENGTH;
break;
// Armor types need Hull and Shields but not Simulated Hull
case OP_SET_ARMOR_TYPE:
special_subsys = OPS_ARMOR;
break;
// awacs subsystems
case OP_AWACS_SET_RADIUS:
special_subsys = OPS_AWACS;
break;
// rotating
case OP_LOCK_ROTATING_SUBSYSTEM:
case OP_FREE_ROTATING_SUBSYSTEM:
case OP_REVERSE_ROTATING_SUBSYSTEM:
case OP_ROTATING_SUBSYS_SET_TURN_TIME:
special_subsys = OPS_ROTATE;
break;
// where we care about capital ship subsystem cargo
case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
special_subsys = OPS_CAP_CARGO;
// get the next sibling
child = tree_nodes[child].next;
break;
// where we care about turrets carrying beam weapons
case OP_BEAM_FIRE:
special_subsys = OPS_BEAM_TURRET;
// if this is arg index 3 (targeted ship)
if(arg_index == 3)
{
special_subsys = OPS_STRENGTH;
child = tree_nodes[child].next;
Assert(child >= 0);
child = tree_nodes[child].next;
}
else
{
Assert(arg_index == 1);
}
break;
case OP_BEAM_FIRE_COORDS:
special_subsys = OPS_BEAM_TURRET;
break;
// these sexps check the subsystem of the *second entry* on the list, not the first
case OP_DISTANCE_SUBSYSTEM:
case OP_SET_CARGO:
case OP_IS_CARGO:
case OP_CHANGE_AI_CLASS:
case OP_IS_AI_CLASS:
case OP_MISSILE_LOCKED:
case OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD:
// iterate to the next field
child = tree_nodes[child].next;
break;
// this sexp checks the subsystem of the *fourth entry* on the list
case OP_QUERY_ORDERS:
child = tree_nodes[child].next;
Assert(child >= 0);
child = tree_nodes[child].next;
Assert(child >= 0);
child = tree_nodes[child].next;
break;
// this sexp checks the subsystem of the *ninth entry* on the list
case OP_WEAPON_CREATE:
// iterate to the next field eight times
child = tree_nodes[child].next;
Assert(child >= 0);
child = tree_nodes[child].next;
Assert(child >= 0);
child = tree_nodes[child].next;
Assert(child >= 0);
child = tree_nodes[child].next;
Assert(child >= 0);
child = tree_nodes[child].next;
Assert(child >= 0);
child = tree_nodes[child].next;
Assert(child >= 0);
child = tree_nodes[child].next;
Assert(child >= 0);
child = tree_nodes[child].next;
break;
}
// now find the ship and add all relevant subsystems
Assert(child >= 0);
sh = ship_name_lookup(tree_nodes[child].text, 1);
if (sh >= 0) {
subsys = GET_FIRST(&Ships[sh].subsys_list);
while (subsys != END_OF_LIST(&Ships[sh].subsys_list)) {
// add stuff
switch(special_subsys){
// subsystem cargo
case OPS_CAP_CARGO:
if (valid_cap_subsys_cargo_list(subsys->system_info->subobj_name) ) {
head.add_data(subsys->system_info->subobj_name);
}
break;
// beam fire
case OPS_BEAM_TURRET:
head.add_data(subsys->system_info->subobj_name);
break;
// awacs level
case OPS_AWACS:
if (subsys->system_info->flags & MSS_FLAG_AWACS) {
head.add_data(subsys->system_info->subobj_name);
}
break;
// rotating
case OPS_ROTATE:
if (subsys->system_info->flags & MSS_FLAG_ROTATES) {
head.add_data(subsys->system_info->subobj_name);
}
break;
// everything else
default:
head.add_data(subsys->system_info->subobj_name);
break;
}
// next subsystem
subsys = GET_NEXT(subsys);
}
}
// if one of the subsystem strength operators, append the Hull string and the Simulated Hull string
if(special_subsys == OPS_STRENGTH){
head.add_data(SEXP_HULL_STRING);
head.add_data(SEXP_SIM_HULL_STRING);
}
// if setting armor type we only need Hull and Shields
if(special_subsys == OPS_ARMOR){
head.add_data(SEXP_HULL_STRING);
head.add_data(SEXP_SHIELD_STRING);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_subsystem_type(int parent_node)
{
int i, child, shipnum, num_added = 0;
sexp_list_item head;
ship_subsys *subsys;
// first child node
child = tree_nodes[parent_node].child;
Assert(child >= 0);
// now find the ship
shipnum = ship_name_lookup(tree_nodes[child].text, 1);
if (shipnum < 0) {
return head.next;
}
// add all relevant subsystem types
for (i = 0; i < SUBSYSTEM_MAX; i++) {
// don't allow these two
if (i == SUBSYSTEM_NONE || i == SUBSYSTEM_UNKNOWN)
continue;
// loop through all ship subsystems
subsys = GET_FIRST(&Ships[shipnum].subsys_list);
while (subsys != END_OF_LIST(&Ships[shipnum].subsys_list)) {
// check if this subsystem is of this type
if (i == subsys->system_info->type) {
// subsystem type is applicable, so add it
head.add_data(Subsystem_types[i]);
num_added++;
break;
}
// next subsystem
subsys = GET_NEXT(subsys);
}
}
// if no subsystem types, go ahead and add NONE (even though it won't be checked)
if (num_added == 0) {
head.add_data(Subsystem_types[SUBSYSTEM_NONE]);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_point()
{
char buf[NAME_LENGTH+8];
SCP_list<waypoint_list>::iterator ii;
int j;
sexp_list_item head;
for (ii = Waypoint_lists.begin(); ii != Waypoint_lists.end(); ++ii)
{
for (j = 0; (uint) j < ii->get_waypoints().size(); ++j)
{
sprintf(buf, "%s:%d", ii->get_name(), j + 1);
head.add_data_dup(buf);
}
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_iff()
{
int i;
sexp_list_item head;
for (i=0; i<Num_iffs; i++)
head.add_data(Iff_info[i].iff_name);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ai_class()
{
int i;
sexp_list_item head;
for (i=0; i<Num_ai_classes; i++)
head.add_data(Ai_class_names[i]);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_support_ship_class()
{
int i;
sexp_list_item head;
head.add_data("<species support ship class>");
for (i=0; i<Num_ship_classes; i++)
{
if (Ship_info[i].flags & SIF_SUPPORT)
{
head.add_data(Ship_info[i].name);
}
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ssm_class()
{
sexp_list_item head;
for (auto it = Ssm_info.cbegin(); it != Ssm_info.cend(); ++it)
{
head.add_data(it->name);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_with_bay()
{
object *objp;
sexp_list_item head;
head.add_data("<no anchor>");
for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) )
{
if ( (objp->type == OBJ_SHIP) || (objp->type == OBJ_START) )
{
// determine if this ship has a docking bay
if (ship_has_dock_bay(objp->instance))
{
head.add_data(Ships[objp->instance].ship_name);
}
}
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_soundtrack_name()
{
int i;
sexp_list_item head;
head.add_data("<No Music>");
for (i=0; i<Num_soundtracks; i++)
{
head.add_data(Soundtracks[i].name);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_arrival_location()
{
int i;
sexp_list_item head;
for (i=0; i<MAX_ARRIVAL_NAMES; i++)
head.add_data(Arrival_location_names[i]);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_departure_location()
{
int i;
sexp_list_item head;
for (i=0; i<MAX_DEPARTURE_NAMES; i++)
head.add_data(Departure_location_names[i]);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_arrival_anchor_all()
{
int i, restrict_to_players;
object *objp;
sexp_list_item head;
for (restrict_to_players = 0; restrict_to_players < 2; restrict_to_players++)
{
for (i = 0; i < Num_iffs; i++)
{
char tmp[NAME_LENGTH + 15];
stuff_special_arrival_anchor_name(tmp, i, restrict_to_players, 0);
head.add_data_dup(tmp);
}
}
for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) )
{
if ( (objp->type == OBJ_SHIP) || (objp->type == OBJ_START) )
{
head.add_data(Ships[objp->instance].ship_name);
}
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ai_goal(int parent_node)
{
int i, n, w, z, child;
sexp_list_item head;
Assert(parent_node >= 0);
child = tree_nodes[parent_node].child;
Assert(child >= 0);
n = ship_name_lookup(tree_nodes[child].text, 1);
if (n >= 0) {
// add operators if it's an ai-goal and ai-goal is allowed for that ship
for (i=0; i<Num_operators; i++) {
if ( (query_operator_return_type(i) == OPR_AI_GOAL) && query_sexp_ai_goal_valid(Operators[i].value, n) )
head.add_op(i);
}
} else {
z = wing_name_lookup(tree_nodes[child].text);
if (z >= 0) {
for (w=0; w<Wings[z].wave_count; w++) {
n = Wings[z].ship_index[w];
// add operators if it's an ai-goal and ai-goal is allowed for that ship
for (i=0; i<Num_operators; i++) {
if ( (query_operator_return_type(i) == OPR_AI_GOAL) && query_sexp_ai_goal_valid(Operators[i].value, n) )
head.add_op(i);
}
}
// when dealing with the special argument add them all. It's up to the FREDder to ensure invalid orders aren't given
} else if (!strcmp(tree_nodes[child].text, SEXP_ARGUMENT_STRING)) {
for (i=0; i<Num_operators; i++) {
if (query_operator_return_type(i) == OPR_AI_GOAL) {
head.add_op(i);
}
}
} else
return NULL; // no valid ship or wing to check against, make nothing available
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_docker_point(int parent_node)
{
int i, z;
sexp_list_item head;
int sh = -1;
Assert(parent_node >= 0);
Assert(!stricmp(tree_nodes[parent_node].text, "ai-dock") || !stricmp(tree_nodes[parent_node].text, "set-docked"));
if (!stricmp(tree_nodes[parent_node].text, "ai-dock"))
{
z = tree_nodes[parent_node].parent;
Assert(z >= 0);
Assert(!stricmp(tree_nodes[z].text, "add-ship-goal") || !stricmp(tree_nodes[z].text, "add-wing-goal") || !stricmp(tree_nodes[z].text, "add-goal"));
z = tree_nodes[z].child;
Assert(z >= 0);
sh = ship_name_lookup(tree_nodes[z].text, 1);
}
else if (!stricmp(tree_nodes[parent_node].text, "set-docked"))
{
//Docker ship should be the first child node
z = tree_nodes[parent_node].child;
sh = ship_name_lookup(tree_nodes[z].text, 1);
}
if (sh >= 0)
{
z = get_docking_list(Ship_info[Ships[sh].ship_info_index].model_num);
for (i=0; i<z; i++)
head.add_data(Docking_bay_list[i]);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_dockee_point(int parent_node)
{
int i, z;
sexp_list_item head;
int sh = -1;
Assert(parent_node >= 0);
Assert(!stricmp(tree_nodes[parent_node].text, "ai-dock") || !stricmp(tree_nodes[parent_node].text, "set-docked"));
if (!stricmp(tree_nodes[parent_node].text, "ai-dock"))
{
z = tree_nodes[parent_node].child;
Assert(z >= 0);
sh = ship_name_lookup(tree_nodes[z].text, 1);
}
else if (!stricmp(tree_nodes[parent_node].text, "set-docked"))
{
//Dockee ship should be the third child node
z = tree_nodes[parent_node].child; // 1
z = tree_nodes[z].next; // 2
z = tree_nodes[z].next; // 3
sh = ship_name_lookup(tree_nodes[z].text, 1);
}
if (sh >= 0)
{
z = get_docking_list(Ship_info[Ships[sh].ship_info_index].model_num);
for (i=0; i<z; i++)
head.add_data(Docking_bay_list[i]);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_message()
{
char *str;
int i;
sexp_list_item head;
if (m_mode == MODE_EVENTS) {
Assert(Event_editor_dlg);
// this for looks a litle strange, but had to do it get rid of a warning. Conditional
//uses last statement is sequence, i.e. same as for (i=0, str, i++)
for (i=0; str = Event_editor_dlg->current_message_name(i), str; i++)
head.add_data(str);
} else {
for (i=Num_builtin_messages; i<Num_messages; i++)
head.add_data(Messages[i].name);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_persona()
{
int i;
sexp_list_item head;
for (i = 0; i < Num_personas; i++) {
if (Personas[i].flags & PERSONA_FLAG_WINGMAN) {
head.add_data(Personas[i].name);
}
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_font()
{
int i;
sexp_list_item head;
for (i = 0; i < Num_fonts; i++) {
head.add_data(Fonts[i].filename);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_who_from()
{
object *ptr;
sexp_list_item head;
//head.add_data("<any allied>");
head.add_data("#Command");
head.add_data("<any wingman>");
head.add_data("<none>");
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START))
if (!(Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].flags & SIF_NOT_FLYABLE))
head.add_data(Ships[ptr->instance].ship_name);
ptr = GET_NEXT(ptr);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_priority()
{
sexp_list_item head;
head.add_data("High");
head.add_data("Normal");
head.add_data("Low");
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_sound_environment()
{
sexp_list_item head;
head.add_data(SEXP_NONE_STRING);
for (int i = 0; i < (int)EFX_presets.size(); i++) {
head.add_data_dup(EFX_presets[i].name.c_str());
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_sound_environment_option()
{
sexp_list_item head;
for (int i = 0; i < Num_sound_environment_options; i++)
head.add_data(Sound_environment_option[i]);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_adjust_audio_volume()
{
sexp_list_item head;
for (int i = 0; i < Num_adjust_audio_options; i++)
head.add_data(Adjust_audio_options[i]);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_hud_gauge()
{
sexp_list_item head;
for (int i = 0; i < Num_hud_gauge_types; i++)
head.add_data(Hud_gauge_types[i].name);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_effect()
{
sexp_list_item head;
for (SCP_vector<ship_effect>::iterator sei = Ship_effects.begin(); sei != Ship_effects.end(); ++sei) {
head.add_data_dup(sei->name);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_explosion_option()
{
sexp_list_item head;
for (int i = 0; i < Num_explosion_options; i++)
head.add_data(Explosion_option[i]);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_waypoint_path()
{
SCP_list<waypoint_list>::iterator ii;
sexp_list_item head;
for (ii = Waypoint_lists.begin(); ii != Waypoint_lists.end(); ++ii)
head.add_data(ii->get_name());
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_point()
{
sexp_list_item head;
head.add_list(get_listing_opf_ship());
head.add_list(get_listing_opf_point());
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_wing_wholeteam()
{
int i;
sexp_list_item head;
for (i = 0; i < Num_iffs; i++)
head.add_data(Iff_info[i].iff_name);
head.add_list(get_listing_opf_ship_wing());
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_wing_shiponteam_point()
{
int i;
sexp_list_item head;
for (i = 0; i < Num_iffs; i++)
{
char tmp[NAME_LENGTH + 7];
sprintf(tmp, "<any %s>", Iff_info[i].iff_name);
strlwr(tmp);
head.add_data_dup(tmp);
}
head.add_list(get_listing_opf_ship_wing_point());
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_wing_point()
{
sexp_list_item head;
head.add_list(get_listing_opf_ship());
head.add_list(get_listing_opf_wing());
head.add_list(get_listing_opf_point());
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_wing_point_or_none()
{
sexp_list_item head;
head.add_data(SEXP_NONE_STRING);
head.add_list(get_listing_opf_ship_wing_point());
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_mission_name()
{
int i;
sexp_list_item head;
if ((m_mode == MODE_CAMPAIGN) && (Cur_campaign_mission >= 0)) {
for (i=0; i<Campaign.num_missions; i++)
if ( (i == Cur_campaign_mission) || (Campaign.missions[i].level < Campaign.missions[Cur_campaign_mission].level) )
head.add_data(Campaign.missions[i].name);
} else
head.add_data(Mission_filename);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_goal_name(int parent_node)
{
int i, m;
sexp_list_item head;
if (m_mode == MODE_CAMPAIGN) {
int child;
Assert(parent_node >= 0);
child = tree_nodes[parent_node].child;
Assert(child >= 0);
for (m=0; m<Campaign.num_missions; m++)
if (!stricmp(Campaign.missions[m].name, tree_nodes[child].text))
break;
if (m < Campaign.num_missions) {
if (Campaign.missions[m].num_goals < 0) // haven't loaded goal names yet.
read_mission_goal_list(m);
for (i=0; i<Campaign.missions[m].num_goals; i++)
head.add_data(Campaign.missions[m].goals[i].name);
}
} else {
for (i=0; i<Num_goals; i++)
head.add_data(Mission_goals[i].name);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_wing()
{
sexp_list_item head;
head.add_list(get_listing_opf_ship());
head.add_list(get_listing_opf_wing());
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_order_recipient()
{
sexp_list_item head;
head.add_data("<all fighters>");
head.add_list(get_listing_opf_ship());
head.add_list(get_listing_opf_wing());
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_type()
{
unsigned int i;
sexp_list_item head;
for (i=0; i<Ship_types.size(); i++){
head.add_data(Ship_types[i].name);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_keypress()
{
int i;
sexp_list_item head;
for (i=0; i<CCFG_MAX; i++) {
if (Control_config[i].key_default > 0 && !Control_config[i].disabled) {
head.add_data_dup(textify_scancode(Control_config[i].key_default));
}
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_event_name(int parent_node)
{
int i, m;
sexp_list_item head;
if (m_mode == MODE_CAMPAIGN) {
int child;
Assert(parent_node >= 0);
child = tree_nodes[parent_node].child;
Assert(child >= 0);
for (m=0; m<Campaign.num_missions; m++)
if (!stricmp(Campaign.missions[m].name, tree_nodes[child].text))
break;
if (m < Campaign.num_missions) {
if (Campaign.missions[m].num_events < 0) // haven't loaded goal names yet.
read_mission_goal_list(m);
for (i=0; i<Campaign.missions[m].num_events; i++)
head.add_data(Campaign.missions[m].events[i].name);
}
} else {
for (i=0; i<Num_mission_events; i++)
head.add_data(Mission_events[i].name);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ai_order()
{
int i;
sexp_list_item head;
for (i=0; i<NUM_COMM_ORDER_ITEMS; i++)
head.add_data(Comm_orders[i].name);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_skill_level()
{
int i;
sexp_list_item head;
for (i=0; i<NUM_SKILL_LEVELS; i++)
head.add_data(Skill_level_names(i, 0));
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_cargo()
{
sexp_list_item head;
head.add_data("Nothing");
for (int i=0; i<Num_cargo; i++)
{
if (stricmp(Cargo_names[i], "nothing"))
head.add_data(Cargo_names[i]);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_string()
{
sexp_list_item head;
head.add_data(SEXP_ANY_STRING);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_medal_name()
{
int i;
sexp_list_item head;
for (i=0; i<Num_medals; i++)
{
// don't add Rank or the Ace badges
if ((i == Rank_medal_index) || (Medals[i].kills_needed > 0))
continue;
head.add_data(Medals[i].name);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_weapon_name()
{
int i;
sexp_list_item head;
for (i=0; i<Num_weapon_types; i++)
head.add_data(Weapon_info[i].name);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_intel_name()
{
int i;
sexp_list_item head;
for (i=0; i<Intel_info_size; i++)
head.add_data(Intel_info[i].name);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_class_name()
{
int i;
sexp_list_item head;
for (i=0; i<Num_ship_classes; i++)
head.add_data(Ship_info[i].name);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_hud_gauge_name()
{
int i;
sexp_list_item head;
for (i=0; i<NUM_HUD_GAUGES; i++)
head.add_data(HUD_gauge_text[i]);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_huge_weapon()
{
int i;
sexp_list_item head;
for (i=0; i<Num_weapon_types; i++) {
if (Weapon_info[i].wi_flags & WIF_HUGE)
head.add_data(Weapon_info[i].name);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_not_player()
{
object *ptr;
sexp_list_item head;
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (ptr->type == OBJ_SHIP)
head.add_data(Ships[ptr->instance].ship_name);
ptr = GET_NEXT(ptr);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_or_none()
{
sexp_list_item head;
head.add_data(SEXP_NONE_STRING);
head.add_list(get_listing_opf_ship());
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_subsystem_or_none(int parent_node, int arg_index)
{
sexp_list_item head;
head.add_data(SEXP_NONE_STRING);
head.add_list(get_listing_opf_subsystem(parent_node, arg_index));
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_subsys_or_generic(int parent_node, int arg_index)
{
sexp_list_item head;
head.add_data(SEXP_ALL_ENGINES_STRING);
head.add_data(SEXP_ALL_TURRETS_STRING);
head.add_list(get_listing_opf_subsystem(parent_node, arg_index));
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_jump_nodes()
{
sexp_list_item head;
SCP_list<CJumpNode>::iterator jnp;
for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
head.add_data( jnp->GetName());
}
return head.next;
}
// creates list of Sexp_variables
sexp_list_item *sexp_tree::get_listing_opf_variable_names()
{
int i;
sexp_list_item head;
for (i=0; i<MAX_SEXP_VARIABLES; i++) {
if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
head.add_data( Sexp_variables[i].variable_name );
}
}
return head.next;
}
// get default skybox model name
sexp_list_item *sexp_tree::get_listing_opf_skybox_model()
{
sexp_list_item head;
head.add_data("default");
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_skybox_flags()
{
sexp_list_item head;
int i;
for (i = 0; i < Num_skybox_flags; ++i) {
head.add_data(Skybox_flags[i]);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_background_bitmap()
{
sexp_list_item head;
int i;
for (i=0; i < stars_get_num_entries(false, true); i++)
{
head.add_data( stars_get_name_FRED(i, false) );
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_sun_bitmap()
{
sexp_list_item head;
int i;
for (i=0; i < stars_get_num_entries(true, true); i++)
{
head.add_data( stars_get_name_FRED(i, true) );
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_nebula_storm_type()
{
sexp_list_item head;
head.add_data(SEXP_NONE_STRING);
for (size_t i=0; i < Storm_types.size(); i++)
{
head.add_data(Storm_types[i].name);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_nebula_poof()
{
sexp_list_item head;
int i;
for (i=0; i < MAX_NEB2_POOFS; i++)
{
head.add_data(Neb2_poof_filenames[i]);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_turret_target_order()
{
int i;
sexp_list_item head;
for (i=0; i<NUM_TURRET_ORDER_TYPES; i++)
head.add_data(Turret_target_order_names[i]);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_post_effect()
{
unsigned int i;
sexp_list_item head;
SCP_vector<SCP_string> ppe_names;
get_post_process_effect_names(ppe_names);
for (i=0; i < ppe_names.size(); i++) {
head.add_data_dup(ppe_names[i].c_str());
}
head.add_data_dup("lightshafts");
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_turret_target_priorities()
{
size_t t;
sexp_list_item head;
for(t = 0; t < Ai_tp_list.size(); t++) {
head.add_data(Ai_tp_list[t].name);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_armor_type()
{
size_t t;
sexp_list_item head;
head.add_data(SEXP_NONE_STRING);
for (t=0; t<Armor_types.size(); t++)
head.add_data(Armor_types[t].GetNamePtr());
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_damage_type()
{
size_t t;
sexp_list_item head;
head.add_data(SEXP_NONE_STRING);
for (t=0; t<Damage_types.size(); t++)
head.add_data(Damage_types[t].name);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_animation_type()
{
size_t t;
sexp_list_item head;
for (t = 0; t < MAX_TRIGGER_ANIMATION_TYPES; t++)
head.add_data(Animation_type_names[t]);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_hud_elements()
{
sexp_list_item head;
head.add_data("warpout");
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_weapon_banks()
{
sexp_list_item head;
head.add_data(SEXP_ALL_BANKS_STRING);
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_mission_moods()
{
sexp_list_item head;
for (SCP_vector<SCP_string>::iterator iter = Builtin_moods.begin(); iter != Builtin_moods.end(); ++iter) {
head.add_data_dup(iter->c_str());
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_ship_flags()
{
int i;
sexp_list_item head;
// object flags
for ( i = 0; i < MAX_OBJECT_FLAG_NAMES; i++) {
head.add_data_dup(Object_flag_names[i].flag_name);
}
// ship flags
for ( i = 0; i < MAX_SHIP_FLAG_NAMES; i++) {
head.add_data_dup(Ship_flag_names[i].flag_name);
}
// ai flags
for ( i = 0; i < MAX_AI_FLAG_NAMES; i++) {
head.add_data_dup(Ai_flag_names[i].flag_name);
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_team_colors()
{
sexp_list_item head;
head.add_data("None");
for (SCP_map<SCP_string, team_color>::iterator tcolor = Team_Colors.begin(); tcolor != Team_Colors.end(); ++tcolor) {
head.add_data_dup(tcolor->first.c_str());
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_nebula_patterns()
{
sexp_list_item head;
head.add_data(SEXP_NONE_STRING);
for (int i = 0; i < MAX_NEB2_BITMAPS; i++) {
if (strlen(Neb2_bitmap_filenames[i]) > 0) {
head.add_data(Neb2_bitmap_filenames[i]);
}
}
return head.next;
}
sexp_list_item *sexp_tree::get_listing_opf_game_snds()
{
sexp_list_item head;
head.add_data(SEXP_NONE_STRING);
for (SCP_vector<game_snd>::iterator iter = Snds.begin(); iter != Snds.end(); ++iter) {
if (!can_construe_as_integer(iter->name.c_str())) {
head.add_data(iter->name.c_str());
}
}
return head.next;
}
// Deletes sexp_variable from sexp_tree.
// resets tree to not include given variable, and resets text and type
void sexp_tree::delete_sexp_tree_variable(const char *var_name)
{
char search_str[64];
char replace_text[TOKEN_LENGTH];
sprintf(search_str, "%s(", var_name);
// store old item index
int old_item_index = item_index;
for (uint idx=0; idx<tree_nodes.size(); idx++) {
if (tree_nodes[idx].type & SEXPT_VARIABLE) {
if ( strstr(tree_nodes[idx].text, search_str) != NULL ) {
// check type is number or string
Assert( (tree_nodes[idx].type & SEXPT_NUMBER) || (tree_nodes[idx].type & SEXPT_STRING) );
// reset type as not variable
int type = tree_nodes[idx].type &= ~SEXPT_VARIABLE;
// reset text
if (tree_nodes[idx].type & SEXPT_NUMBER) {
strcpy_s(replace_text, "number");
} else {
strcpy_s(replace_text, "string");
}
// set item_index and replace data
item_index = idx;
replace_data(replace_text, type);
}
}
}
// restore item_index
item_index = old_item_index;
}
// Modify sexp_tree for a change in sexp_variable (name, type, or default value)
void sexp_tree::modify_sexp_tree_variable(const char *old_name, int sexp_var_index)
{
char search_str[64];
int type;
Assert(Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_SET);
Assert( (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) || (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) );
// Get type for sexp_tree node
if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
type = (SEXPT_NUMBER | SEXPT_VALID);
} else {
type = (SEXPT_STRING | SEXPT_VALID);
}
// store item index;
int old_item_index = item_index;
// Search string in sexp_tree nodes
sprintf(search_str, "%s(", old_name);
for (uint idx=0; idx<tree_nodes.size(); idx++) {
if (tree_nodes[idx].type & SEXPT_VARIABLE) {
if ( strstr(tree_nodes[idx].text, search_str) != NULL ) {
// temp set item_index
item_index = idx;
// replace variable data
replace_variable_data(sexp_var_index, (type | SEXPT_VARIABLE));
}
}
}
// restore item_index
item_index = old_item_index;
}
// convert from item_index to sexp_variable index, -1 if not
int sexp_tree::get_item_index_to_var_index()
{
// check valid item index and node is a variable
if ( (item_index > 0) && (tree_nodes[item_index].type & SEXPT_VARIABLE) ) {
return get_tree_name_to_sexp_variable_index(tree_nodes[item_index].text);
} else {
return -1;
}
}
int sexp_tree::get_tree_name_to_sexp_variable_index(const char *tree_name)
{
char var_name[TOKEN_LENGTH];
int chars_to_copy = strcspn(tree_name, "(");
Assert(chars_to_copy < TOKEN_LENGTH - 1);
// Copy up to '(' and add null termination
strncpy(var_name, tree_name, chars_to_copy);
var_name[chars_to_copy] = '\0';
// Look up index
return get_index_sexp_variable_name(var_name);
}
int sexp_tree::get_variable_count(const char *var_name)
{
uint idx;
int count = 0;
char compare_name[64];
// get name to compare
strcpy_s(compare_name, var_name);
strcat_s(compare_name, "(");
// look for compare name
for (idx=0; idx<tree_nodes.size(); idx++) {
if (tree_nodes[idx].type & SEXPT_VARIABLE) {
if ( strstr(tree_nodes[idx].text, compare_name) ) {
count++;
}
}
}
return count;
}
// Returns the number of times a variable with this name has been used by player loadout
int sexp_tree::get_loadout_variable_count(int var_index)
{
// we shouldn't be being passed the index of variables that do not exist
Assert (var_index >= 0 && var_index < MAX_SEXP_VARIABLES);
int idx;
int count = 0;
for (int i=0; i < MAX_TVT_TEAMS; i++) {
for(idx=0; idx<Team_data[i].num_ship_choices; idx++) {
if (!strcmp(Team_data[i].ship_list_variables[idx], Sexp_variables[var_index].variable_name)) {
count++;
}
if (!strcmp(Team_data[i].ship_count_variables[idx], Sexp_variables[var_index].variable_name)) {
count++;
}
}
for (idx=0; idx<Team_data[i].num_weapon_choices; idx++) {
if (!strcmp(Team_data[i].weaponry_pool_variable[idx], Sexp_variables[var_index].variable_name)) {
count++;
}
if (!strcmp(Team_data[i].weaponry_amount_variable[idx], Sexp_variables[var_index].variable_name)) {
count++;
}
}
}
return count;
}