1 /*
2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9
10
11
12 #include "sexp_tree.h"
13 #include "mission/util.h"
14 #include "mission/Editor.h"
15 #include "mission/object.h"
16
17 #include "parse/sexp.h"
18 #include "globalincs/linklist.h"
19 #include "ai/aigoals.h"
20 #include "mission/missionmessage.h"
21 #include "mission/missioncampaign.h"
22 #include "hud/hudsquadmsg.h"
23 #include "stats/medals.h"
24 #include "controlconfig/controlsconfig.h"
25 #include "hud/hudgauges.h"
26 #include "starfield/starfield.h"
27 #include "nebula/neb.h"
28 #include "nebula/neblightning.h"
29 #include "jumpnode/jumpnode.h"
30 #include "gamesnd/eventmusic.h" // for change-soundtrack
31 #include "menuui/techmenu.h" // for intel stuff
32 #include "weapon/emp.h"
33 #include "gamesnd/gamesnd.h"
34 #include "weapon/weapon.h"
35 #include "hud/hudartillery.h"
36 #include "iff_defs/iff_defs.h"
37 #include "mission/missionmessage.h"
38 #include "sound/ds.h"
39 #include "globalincs/alphacolors.h"
40 #include "localization/localize.h"
41 #include "mission/missiongoals.h"
42 #include "ship/ship.h"
43
44 #include <ui/util/menu.h>
45 #include <ui/util/SignalBlockers.h>
46
47 #include <QtWidgets/QMessageBox>
48 #include <QtWidgets/QMenu>
49 #include <QDebug>
50
51 #define TREE_NODE_INCREMENT 100
52
53 #define MAX_OP_MENUS 30
54 #define MAX_SUBMENUS (MAX_OP_MENUS * MAX_OP_MENUS)
55
56 extern SCP_vector<game_snd> Snds;
57
58 //********************sexp_tree********************
59
60 namespace fso {
61 namespace fred {
62
63 namespace {
node_image_to_resource_name(NodeImage image)64 QString node_image_to_resource_name(NodeImage image) {
65 switch (image) {
66 case NodeImage::OPERATOR:
67 return ":/images/bitmap1.png";
68 case NodeImage::DATA:
69 return ":/images/data.png";
70 case NodeImage::VARIABLE:
71 return ":/images/variable.png";
72 case NodeImage::ROOT:
73 return ":/images/root.png";
74 case NodeImage::ROOT_DIRECTIVE:
75 return ":/images/root_directive.png";
76 case NodeImage::CHAIN:
77 return ":/images/chained.png";
78 case NodeImage::CHAIN_DIRECTIVE:
79 return ":/images/chained_directive.png";
80 case NodeImage::GREEN_DOT:
81 return ":/images/green_do.png";
82 case NodeImage::BLACK_DOT:
83 return ":/images/black_do.png";
84 case NodeImage::DATA_00:
85 return ":/images/data00.png";
86 case NodeImage::DATA_05:
87 return ":/images/data05.png";
88 case NodeImage::DATA_10:
89 return ":/images/data10.png";
90 case NodeImage::DATA_15:
91 return ":/images/data15.png";
92 case NodeImage::DATA_20:
93 return ":/images/data20.png";
94 case NodeImage::DATA_25:
95 return ":/images/data25.png";
96 case NodeImage::DATA_30:
97 return ":/images/data30.png";
98 case NodeImage::DATA_35:
99 return ":/images/data35.png";
100 case NodeImage::DATA_40:
101 return ":/images/data40.png";
102 case NodeImage::DATA_45:
103 return ":/images/data45.png";
104 case NodeImage::DATA_50:
105 return ":/images/data50.png";
106 case NodeImage::DATA_55:
107 return ":/images/data55.png";
108 case NodeImage::DATA_60:
109 return ":/images/data60.png";
110 case NodeImage::DATA_65:
111 return ":/images/data65.png";
112 case NodeImage::DATA_70:
113 return ":/images/data70.png";
114 case NodeImage::DATA_75:
115 return ":/images/data75.png";
116 case NodeImage::DATA_80:
117 return ":/images/data80.png";
118 case NodeImage::DATA_85:
119 return ":/images/data85.png";
120 case NodeImage::DATA_90:
121 return ":/images/data90.png";
122 case NodeImage::DATA_95:
123 return ":/images/data95.png";
124 case NodeImage::COMMENT:
125 return ":/images/comment.png";
126 }
127 return ":/images/bitmap1.png";
128 }
129 }
130
SexpTreeEditorInterface()131 SexpTreeEditorInterface::SexpTreeEditorInterface() :
132 SexpTreeEditorInterface(flagset<TreeFlags>{ TreeFlags::LabeledRoot, TreeFlags::RootDeletable }) {
133 }
SexpTreeEditorInterface(const flagset<TreeFlags> & flags)134 SexpTreeEditorInterface::SexpTreeEditorInterface(const flagset<TreeFlags>& flags) : _flags(flags) {
135
136 }
hasDefaultMessageParamter()137 bool SexpTreeEditorInterface::hasDefaultMessageParamter() {
138 return Num_messages > Num_builtin_messages;
139 }
getMessages()140 SCP_vector<SCP_string> SexpTreeEditorInterface::getMessages() {
141 SCP_vector<SCP_string> list;
142
143 for (auto i = Num_builtin_messages; i < Num_messages; i++) {
144 list.emplace_back(Messages[i].name);
145 }
146
147 return list;
148 }
getMissionGoals(const SCP_string &)149 SCP_vector<SCP_string> SexpTreeEditorInterface::getMissionGoals(const SCP_string& /*reference_name*/) {
150 SCP_vector<SCP_string> list;
151
152 for (auto i = 0; i < Num_goals; i++) {
153 list.emplace_back(Mission_goals[i].name);
154 }
155
156 return list;
157 }
getMissionEvents(const SCP_string &)158 SCP_vector<SCP_string> SexpTreeEditorInterface::getMissionEvents(const SCP_string& /*reference_name*/) {
159 SCP_vector<SCP_string> list;
160
161 for (auto i = 0; i < Num_mission_events; i++) {
162 list.emplace_back(Mission_events[i].name);
163 }
164
165 return list;
166 }
getMissionNames()167 SCP_vector<SCP_string> SexpTreeEditorInterface::getMissionNames() {
168 return { SCP_string(Mission_filename) };
169 }
hasDefaultMissionName()170 bool SexpTreeEditorInterface::hasDefaultMissionName() {
171 return *Mission_filename != '\0';
172 }
hasDefaultGoal(int operator_value)173 bool SexpTreeEditorInterface::hasDefaultGoal(int operator_value) {
174 return (operator_value == OP_PREVIOUS_GOAL_TRUE) || (operator_value == OP_PREVIOUS_GOAL_FALSE)
175 || (operator_value == OP_PREVIOUS_GOAL_INCOMPLETE) || Num_goals;
176 }
hasDefaultEvent(int operator_value)177 bool SexpTreeEditorInterface::hasDefaultEvent(int operator_value) {
178 return (operator_value == OP_PREVIOUS_EVENT_TRUE) || (operator_value == OP_PREVIOUS_EVENT_FALSE)
179 || (operator_value == OP_PREVIOUS_EVENT_INCOMPLETE) || Num_mission_events;
180
181 }
getFlags() const182 const flagset<TreeFlags>& SexpTreeEditorInterface::getFlags() const {
183 return _flags;
184 }
getRootReturnType() const185 int SexpTreeEditorInterface::getRootReturnType() const {
186 return OPR_BOOL;
187 }
requireCampaignOperators() const188 bool SexpTreeEditorInterface::requireCampaignOperators() const {
189 return false;
190 }
191 SexpTreeEditorInterface::~SexpTreeEditorInterface() = default;
192
convertNodeImageToIcon(NodeImage image)193 QIcon sexp_tree::convertNodeImageToIcon(NodeImage image) {
194 return QIcon(node_image_to_resource_name(image));
195 }
196
197 // constructor
sexp_tree(QWidget * parent)198 sexp_tree::sexp_tree(QWidget* parent) : QTreeWidget(parent) {
199 setSelectionMode(QTreeWidget::SingleSelection);
200 setSelectionBehavior(QTreeWidget::SelectItems);
201
202 setContextMenuPolicy(Qt::CustomContextMenu);
203
204 setHeaderHidden(true);
205
206 select_sexp_node = -1;
207 root_item = -1;
208 clear_tree();
209
210 connect(this, &QWidget::customContextMenuRequested, this, &sexp_tree::customMenuHandler);
211 connect(this, &QTreeWidget::itemChanged, this, &sexp_tree::handleItemChange);
212 connect(this, &QTreeWidget::itemSelectionChanged, this, &sexp_tree::handleNewItemSelected);
213 }
214
215 sexp_tree::~sexp_tree() = default;
216
217 // clears out the tree, so all the nodes are unused.
clear_tree(const char * op)218 void sexp_tree::clear_tree(const char* op) {
219 mprintf(("Resetting dynamic tree node limit from "
220 SIZE_T_ARG
221 " to %d...\n", tree_nodes.size(), 0));
222
223 total_nodes = flag = 0;
224 tree_nodes.clear();
225
226 if (op) {
227 clear();
228 if (strlen(op)) {
229 set_node(allocate_node(-1), (SEXPT_OPERATOR | SEXPT_VALID), op);
230 build_tree();
231 }
232 }
233 }
234
reset_handles()235 void sexp_tree::reset_handles() {
236 uint i;
237
238 for (i = 0; i < tree_nodes.size(); i++) {
239 tree_nodes[i].handle = NULL;
240 }
241 }
242
243 // initializes and creates a tree from a given sexp startpoint.
load_tree(int index,const char * deflt)244 void sexp_tree::load_tree(int index, const char* deflt) {
245 int cur;
246
247 clear_tree();
248 root_item = 0;
249 if (index < 0) {
250 cur = allocate_node(-1);
251 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), deflt); // setup a default tree if none
252 build_tree();
253 return;
254 }
255
256 if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) { // handle numbers allender likes to use so much..
257 cur = allocate_node(-1);
258 if (atoi(Sexp_nodes[index].text)) {
259 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "true");
260 } else {
261 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "false");
262 }
263
264 build_tree();
265 return;
266 }
267
268 // assumption: first token is an operator. I require this because it would cause problems
269 // with child/parent relations otherwise, and it should be this way anyway, since the
270 // return type of the whole sexp is boolean, and only operators can satisfy this.
271 Assert(Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR);
272 load_branch(index, -1);
273 build_tree();
274 }
275
get_combined_variable_name(char * combined_name,const char * sexp_var_name)276 void get_combined_variable_name(char* combined_name, const char* sexp_var_name) {
277 int sexp_var_index = get_index_sexp_variable_name(sexp_var_name);
278 Assert(sexp_var_index > -1);
279
280 sprintf(combined_name, "%s(%s)", Sexp_variables[sexp_var_index].variable_name, Sexp_variables[sexp_var_index].text);
281 }
282
283 // creates a tree from a given Sexp_nodes[] point under a given parent. Recursive.
284 // Returns the allocated current node.
load_branch(int index,int parent)285 int sexp_tree::load_branch(int index, int parent) {
286 int cur = -1;
287 char combined_var_name[2 * TOKEN_LENGTH + 2];
288
289 while (index != -1) {
290 Assert(Sexp_nodes[index].type != SEXP_NOT_USED);
291 if (Sexp_nodes[index].subtype == SEXP_ATOM_LIST) {
292 load_branch(Sexp_nodes[index].first, parent); // do the sublist and continue
293
294 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR) {
295 cur = allocate_node(parent);
296 if ((index == select_sexp_node) && !flag) { // translate sexp node to our node
297 select_sexp_node = cur;
298 flag = 1;
299 }
300
301 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), Sexp_nodes[index].text);
302 load_branch(Sexp_nodes[index].rest, cur); // operator is new parent now
303 return cur; // 'rest' was just used, so nothing left to use.
304
305 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) {
306 cur = allocate_node(parent);
307 if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
308 get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
309 set_node(cur, (SEXPT_VARIABLE | SEXPT_NUMBER | SEXPT_VALID), combined_var_name);
310 } else {
311 set_node(cur, (SEXPT_NUMBER | SEXPT_VALID), Sexp_nodes[index].text);
312 }
313
314 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_STRING) {
315 cur = allocate_node(parent);
316 if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
317 get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
318 set_node(cur, (SEXPT_VARIABLE | SEXPT_STRING | SEXPT_VALID), combined_var_name);
319 } else {
320 set_node(cur, (SEXPT_STRING | SEXPT_VALID), Sexp_nodes[index].text);
321 }
322
323 } else
324 Assert(0); // unknown and/or invalid sexp type
325
326 if ((index == select_sexp_node) && !flag) { // translate sexp node to our node
327 select_sexp_node = cur;
328 flag = 1;
329 }
330
331 index = Sexp_nodes[index].rest;
332 if (index == -1) {
333 return cur;
334 }
335 }
336
337 return cur;
338 }
339
query_false(int node)340 int sexp_tree::query_false(int node) {
341 if (node < 0) {
342 node = root_item;
343 }
344
345 Assert(node >= 0);
346 Assert(tree_nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID));
347 Assert(tree_nodes[node].next == -1); // must make this assumption or else it will confuse code!
348 if (get_operator_const(tree_nodes[node].text) == OP_FALSE) {
349 return TRUE;
350 }
351
352 return FALSE;
353 }
354
355 // builds an sexp of the tree and returns the index of it. This allocates sexp nodes.
save_tree(int node)356 int sexp_tree::save_tree(int node) {
357 if (node < 0) {
358 node = root_item;
359 }
360
361 Assert(node >= 0);
362 Assert(tree_nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID));
363 Assert(tree_nodes[node].next == -1); // must make this assumption or else it will confuse code!
364 return save_branch(node);
365 }
366
367 // get variable name from sexp_tree node .text
var_name_from_sexp_tree_text(char * var_name,const char * text)368 void var_name_from_sexp_tree_text(char* var_name, const char* text) {
369 auto var_name_length = strcspn(text, "(");
370 Assert(var_name_length < TOKEN_LENGTH - 1);
371
372 strncpy(var_name, text, var_name_length);
373 var_name[var_name_length] = '\0';
374 }
375
376 #define NO_PREVIOUS_NODE -9
377 // called recursively to save a tree branch and everything under it
save_branch(int cur,int at_root)378 int sexp_tree::save_branch(int cur, int at_root) {
379 int start, node = -1, last = NO_PREVIOUS_NODE;
380 char var_name_text[TOKEN_LENGTH];
381
382 start = -1;
383 while (cur != -1) {
384 if (tree_nodes[cur].type & SEXPT_OPERATOR) {
385 node =
386 alloc_sexp(tree_nodes[cur].text, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, save_branch(tree_nodes[cur].child));
387
388 if ((tree_nodes[cur].parent >= 0) && !at_root) {
389 node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1);
390 }
391 } else if (tree_nodes[cur].type & SEXPT_NUMBER) {
392 // allocate number, maybe variable
393 if (tree_nodes[cur].type & SEXPT_VARIABLE) {
394 var_name_from_sexp_tree_text(var_name_text, tree_nodes[cur].text);
395 node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1);
396 } else {
397 node = alloc_sexp(tree_nodes[cur].text, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1);
398 }
399 } else if (tree_nodes[cur].type & SEXPT_STRING) {
400 // allocate string, maybe variable
401 if (tree_nodes[cur].type & SEXPT_VARIABLE) {
402 var_name_from_sexp_tree_text(var_name_text, tree_nodes[cur].text);
403 node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1);
404 } else {
405 node = alloc_sexp(tree_nodes[cur].text, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
406 }
407 } else {
408 Assert(0); // unknown and/or invalid type
409 }
410
411 if (last == NO_PREVIOUS_NODE) {
412 start = node;
413 } else if (last >= 0) {
414 Sexp_nodes[last].rest = node;
415 }
416
417 last = node;
418 Assert(last != NO_PREVIOUS_NODE); // should be impossible
419 cur = tree_nodes[cur].next;
420 if (at_root) {
421 return start;
422 }
423 }
424
425 return start;
426 }
427
428 // find the next free tree node and return its index.
find_free_node()429 int sexp_tree::find_free_node() {
430 int i;
431
432 for (i = 0; i < (int) tree_nodes.size(); i++) {
433 if (tree_nodes[i].type == SEXPT_UNUSED) {
434 return i;
435 }
436 }
437
438 return -1;
439 }
440
441 // allocate a node. Remains used until freed.
allocate_node()442 int sexp_tree::allocate_node() {
443 int node = find_free_node();
444
445 // need more tree nodes?
446 if (node < 0) {
447 int old_size = (int) tree_nodes.size();
448
449 Assert(TREE_NODE_INCREMENT > 0);
450
451 // allocate in blocks of TREE_NODE_INCREMENT
452 tree_nodes.resize(tree_nodes.size() + TREE_NODE_INCREMENT);
453
454 mprintf(("Bumping dynamic tree node limit from %d to "
455 SIZE_T_ARG
456 "...\n", old_size, tree_nodes.size()));
457
458 #ifndef NDEBUG
459 for (int i = old_size; i < (int) tree_nodes.size(); i++) {
460 sexp_tree_item* item = &tree_nodes[i];
461 Assert(item->type == SEXPT_UNUSED);
462 }
463 #endif
464
465 // our new sexp is the first out of the ones we just created
466 node = old_size;
467 }
468
469 // reset the new node
470 tree_nodes[node].type = SEXPT_UNINIT;
471 tree_nodes[node].parent = -1;
472 tree_nodes[node].child = -1;
473 tree_nodes[node].next = -1;
474 tree_nodes[node].flags = 0;
475 strcpy_s(tree_nodes[node].text, "<uninitialized tree node>");
476 tree_nodes[node].handle = NULL;
477
478 total_nodes++;
479 return node;
480 }
481
482 // allocate a child node under 'parent'. Appends to end of list.
allocate_node(int parent,int after)483 int sexp_tree::allocate_node(int parent, int after) {
484 int i, index = allocate_node();
485
486 if (parent != -1) {
487 i = tree_nodes[parent].child;
488 if (i == -1) {
489 tree_nodes[parent].child = index;
490
491 } else {
492 while ((i != after) && (tree_nodes[i].next != -1)) {
493 i = tree_nodes[i].next;
494 }
495
496 tree_nodes[index].next = tree_nodes[i].next;
497 tree_nodes[i].next = index;
498 }
499 }
500
501 tree_nodes[index].parent = parent;
502 return index;
503 }
504
505 // free a node and all its children. Also clears pointers to it, if any.
506 // node = node chain to free
507 // cascade = 0: free just this node and children under it. (default)
508 // !0: free this node and all siblings after it.
509 //
free_node(int node,int cascade)510 void sexp_tree::free_node(int node, int cascade) {
511 int i;
512
513 // clear the pointer to node
514 i = tree_nodes[node].parent;
515 Assert(i != -1);
516 if (tree_nodes[i].child == node) {
517 tree_nodes[i].child = tree_nodes[node].next;
518 } else {
519 i = tree_nodes[i].child;
520 while (tree_nodes[i].next != -1) {
521 if (tree_nodes[i].next == node) {
522 tree_nodes[i].next = tree_nodes[node].next;
523 break;
524 }
525
526 i = tree_nodes[i].next;
527 }
528 }
529
530 if (!cascade) {
531 tree_nodes[node].next = -1;
532 }
533
534 // now free up the node and its children
535 free_node2(node);
536 }
537
538 // more simple node freer, which works recursively. It frees the given node and all siblings
539 // that come after it, as well as all children of these. Doesn't clear any links to any of
540 // these freed nodes, so make sure all links are broken first. (i.e. use free_node() if you can)
541 //
free_node2(int node)542 void sexp_tree::free_node2(int node) {
543 Assert(node != -1);
544 Assert(tree_nodes[node].type != SEXPT_UNUSED);
545 Assert(total_nodes > 0);
546 modified();
547 tree_nodes[node].type = SEXPT_UNUSED;
548 total_nodes--;
549 if (tree_nodes[node].child != -1) {
550 free_node2(tree_nodes[node].child);
551 }
552
553 if (tree_nodes[node].next != -1) {
554 free_node2(tree_nodes[node].next);
555 }
556 }
557
558 // initialize the data for a node. Should be called right after a new node is allocated.
set_node(int node,int type,const char * text)559 void sexp_tree::set_node(int node, int type, const char* text) {
560 Assert(type != SEXPT_UNUSED);
561 Assert(tree_nodes[node].type != SEXPT_UNUSED);
562 tree_nodes[node].type = type;
563 size_t max_length;
564 if (type & SEXPT_VARIABLE) {
565 max_length = 2 * TOKEN_LENGTH + 2;
566 } else {
567 max_length = TOKEN_LENGTH;
568 }
569 Assert(strlen(text) < max_length);
570 strcpy_s(tree_nodes[node].text, text);
571 }
572
post_load()573 void sexp_tree::post_load() {
574 if (!flag) {
575 select_sexp_node = -1;
576 }
577 }
578
579 // build or rebuild a CTreeCtrl object with the current tree data
build_tree()580 void sexp_tree::build_tree() {
581 if (!flag) {
582 select_sexp_node = -1;
583 }
584
585 clear();
586 add_sub_tree(0, nullptr);
587 }
588
589 // Create the CTreeCtrl tree from the tree data. The tree data should already be setup by
590 // this point.
add_sub_tree(int node,QTreeWidgetItem * root)591 void sexp_tree::add_sub_tree(int node, QTreeWidgetItem* root) {
592 // char str[80];
593 int node2;
594
595 Assert(node >= 0 && node < (int) tree_nodes.size());
596 node2 = tree_nodes[node].child;
597
598 // check for single argument operator case (prints as one line)
599 /* if (node2 != -1 && tree_nodes[node2].child == -1 && tree_nodes[node2].next == -1) {
600 sprintf(str, "%s %s", tree_nodes[node].text, tree_nodes[node2].text);
601 tree_nodes[node].handle = insert(str, root);
602 tree_nodes[node].flags = OPERAND | EDITABLE;
603 tree_nodes[node2].flags = COMBINED;
604 return;
605 }*/
606
607 // bitmap to draw in tree
608 NodeImage bitmap;
609
610 if (tree_nodes[node].type & SEXPT_OPERATOR) {
611 tree_nodes[node].flags = OPERAND;
612 bitmap = NodeImage::OPERATOR;
613 } else {
614 if (tree_nodes[node].type & SEXPT_VARIABLE) {
615 tree_nodes[node].handle->setFlags(tree_nodes[node].handle->flags().setFlag(Qt::ItemIsEditable, false));
616 tree_nodes[node].flags = NOT_EDITABLE;
617 bitmap = NodeImage::VARIABLE;
618 } else {
619 tree_nodes[node].handle->setFlags(tree_nodes[node].handle->flags().setFlag(Qt::ItemIsEditable, true));
620 tree_nodes[node].flags = EDITABLE;
621 bitmap = get_data_image(node);
622 }
623 }
624
625 root = tree_nodes[node].handle = insert(tree_nodes[node].text, bitmap, root);
626
627 node = node2;
628 while (node != -1) {
629 Assert(node >= 0 && node < (int) tree_nodes.size());
630 Assert(tree_nodes[node].type & SEXPT_VALID);
631 if (tree_nodes[node].type & SEXPT_OPERATOR) {
632 add_sub_tree(node, root);
633
634 } else {
635 Assert(tree_nodes[node].child == -1);
636 if (tree_nodes[node].type & SEXPT_VARIABLE) {
637 tree_nodes[node].handle = insert(tree_nodes[node].text, NodeImage::VARIABLE, root);
638 tree_nodes[node].handle->setFlags(tree_nodes[node].handle->flags().setFlag(Qt::ItemIsEditable, false));
639 tree_nodes[node].flags = NOT_EDITABLE;
640 } else {
641 auto bmap = get_data_image(node);
642 tree_nodes[node].handle = insert(tree_nodes[node].text, bmap, root);
643 tree_nodes[node].flags = EDITABLE;
644 }
645 }
646
647 node = tree_nodes[node].next;
648 }
649 }
650
651 // construct tree nodes for an sexp, adding them to the list and returning first node
load_sub_tree(int index,bool valid,const char * text)652 int sexp_tree::load_sub_tree(int index, bool valid, const char* text) {
653 int cur;
654
655 if (index < 0) {
656 cur = allocate_node(-1);
657 set_node(cur, (SEXPT_OPERATOR | (valid ? SEXPT_VALID : 0)), text); // setup a default tree if none
658 return cur;
659 }
660
661 // assumption: first token is an operator. I require this because it would cause problems
662 // with child/parent relations otherwise, and it should be this way anyway, since the
663 // return type of the whole sexp is boolean, and only operators can satisfy this.
664 Assert(Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR);
665 cur = load_branch(index, -1);
666 return cur;
667 }
668
669 // counts the number of arguments an operator has. Call this with the node of the first
670 // argument of the operator
count_args(int node)671 int sexp_tree::count_args(int node) {
672 int count = 0;
673
674 while (node != -1) {
675 count++;
676 node = tree_nodes[node].next;
677 }
678
679 return count;
680 }
681
682 // identify what type of argument this is. You call it with the node of the first argument
683 // of an operator. It will search through enough of the arguments to determine what type of
684 // data they are.
identify_arg_type(int node)685 int sexp_tree::identify_arg_type(int node) {
686 int type = -1;
687
688 while (node != -1) {
689 Assert(tree_nodes[node].type & SEXPT_VALID);
690 switch (SEXPT_TYPE(tree_nodes[node].type)) {
691 case SEXPT_OPERATOR:
692 type = get_operator_const(tree_nodes[node].text);
693 Assert(type);
694 return query_operator_return_type(type);
695
696 case SEXPT_NUMBER:
697 return OPR_NUMBER;
698
699 case SEXPT_STRING: // either a ship or a wing
700 type = SEXP_ATOM_STRING;
701 break; // don't return, because maybe we can narrow selection down more.
702 }
703
704 node = tree_nodes[node].next;
705 }
706
707 return type;
708 }
709
710 // given a tree node, returns the argument type it should be.
query_node_argument_type(int node)711 int sexp_tree::query_node_argument_type(int node) {
712 int argnum = 0;
713 int parent_node = tree_nodes[node].parent;
714 Assert(parent_node >= 0);
715 argnum = find_argument_number(parent_node, node);
716 int op_num = get_operator_index(tree_nodes[parent_node].text);
717 return query_operator_argument_type(op_num, argnum);
718 }
719
720 //
721 // See https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++
722 //
723 template<typename T>
GeneralizedLevensteinDistance(const T & source,const T & target,typename T::size_type insert_cost=1,typename T::size_type delete_cost=1,typename T::size_type replace_cost=1)724 typename T::size_type GeneralizedLevensteinDistance(const T &source,
725 const T &target,
726 typename T::size_type insert_cost = 1,
727 typename T::size_type delete_cost = 1,
728 typename T::size_type replace_cost = 1) {
729 if (source.size() > target.size()) {
730 return GeneralizedLevensteinDistance(target, source, delete_cost, insert_cost, replace_cost);
731 }
732
733 using TSizeType = typename T::size_type;
734 const TSizeType min_size = source.size(), max_size = target.size();
735 std::vector<TSizeType> lev_dist(min_size + 1);
736
737 lev_dist[0] = 0;
738 for (TSizeType i = 1; i <= min_size; ++i) {
739 lev_dist[i] = lev_dist[i - 1] + delete_cost;
740 }
741
742 for (TSizeType j = 1; j <= max_size; ++j) {
743 TSizeType previous_diagonal = lev_dist[0], previous_diagonal_save;
744 lev_dist[0] += insert_cost;
745
746 for (TSizeType i = 1; i <= min_size; ++i) {
747 previous_diagonal_save = lev_dist[i];
748 if (source[i - 1] == target[j - 1]) {
749 lev_dist[i] = previous_diagonal;
750 }
751 else {
752 lev_dist[i] = std::min(std::min(lev_dist[i - 1] + delete_cost, lev_dist[i] + insert_cost), previous_diagonal + replace_cost);
753 }
754 previous_diagonal = previous_diagonal_save;
755 }
756 }
757
758 return lev_dist[min_size];
759 }
760
761 // Look for the valid operator that is the closest match for 'str' and return the operator
762 // number of it. What operators are valid is determined by 'node', and an operator is valid
763 // if it is allowed to fit at position 'node'
764 //
match_closest_operator(const SCP_string & str,int node)765 SCP_string sexp_tree::match_closest_operator(const SCP_string &str, int node) {
766 int z, i, op, arg_num, opf, opr;
767 int min = -1, best = -1;
768
769 z = tree_nodes[node].parent;
770 if (z < 0) {
771 return str;
772 }
773
774 op = get_operator_index(tree_nodes[z].text);
775 if (op < 0) {
776 return str;
777 }
778
779 // determine which argument we are of the parent
780 arg_num = find_argument_number(z, node);
781 opf = query_operator_argument_type(op, arg_num); // check argument type at this position
782
783 for (i = 0; i < (int) Operators.size(); i++) {
784 opr = query_operator_return_type(i); // figure out which type this operator returns
785
786 if (sexp_query_type_match(opf, opr)) {
787 int dist = (int)GeneralizedLevensteinDistance(str, Operators[i].text, 2, 2, 3);
788 if (min < 0 || dist < min) {
789 min = dist;
790 best = i;
791 }
792 }
793 }
794
795 Assert(best >= 0); // we better have some valid operator at this point.
796 return Operators[best].text;
797 }
798
799 // adds to or replaces (based on passed in flag) the current operator
add_or_replace_operator(int op,int replace_flag)800 void sexp_tree::add_or_replace_operator(int op, int replace_flag) {
801 int i, op2;
802
803 if (replace_flag) {
804 if (tree_nodes[item_index].flags & OPERAND) { // are both operators?
805 op2 = get_operator_index(tree_nodes[item_index].text);
806 Assert(op2 >= 0);
807 i = count_args(tree_nodes[item_index].child);
808 if ((i >= Operators[op].min) && (i <= Operators[op].max)) { // are old num args valid?
809 while (i--) {
810 if (query_operator_argument_type(op2, i)
811 != query_operator_argument_type(op, i)) { // does each arg match expected type?
812 break;
813 }
814 }
815
816 if (i < 0) { // everything is ok, so we can keep old arguments with new operator
817 set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text.c_str());
818 tree_nodes[item_index].handle->setText(0, QString::fromStdString(Operators[op].text));
819 tree_nodes[item_index].flags = OPERAND;
820 return;
821 }
822 }
823 }
824
825 replace_operator(Operators[op].text.c_str());
826
827 } else {
828 add_operator(Operators[op].text.c_str());
829 }
830
831 // fill in all the required (minimum) arguments with default values
832 for (i = 0; i < Operators[op].min; i++) {
833 add_default_operator(op, i);
834 }
835 }
836
837 // initialize node, type operator
838 //
set_op(int op_num)839 void sexp_list_item::set_op(int op_num) {
840 int i;
841
842 if (op_num >= FIRST_OP) { // do we have an op value instead of an op number (index)?
843 for (i = 0; i < (int) Operators.size(); i++) {
844 if (op_num == Operators[i].value) {
845 op_num = i;
846 }
847 } // convert op value to op number
848 }
849
850 op = op_num;
851 text = Operators[op].text;
852 type = (SEXPT_OPERATOR | SEXPT_VALID);
853 }
854
855 // initialize node, type data
856 // Defaults: t = SEXPT_STRING
857 //
set_data(const char * str,int t)858 void sexp_list_item::set_data(const char* str, int t) {
859 op = -1;
860 text = str;
861 type = t;
862 }
863
864 // initialize node, type data, allocating memory for the text
865 // Defaults: t = SEXPT_STRING
866 //
set_data_dup(const char * str,int t)867 void sexp_list_item::set_data_dup(const char* str, int t) {
868 op = -1;
869 text = strdup(str);
870 flags |= SEXP_ITEM_F_DUP;
871 type = t;
872 }
873
874 // add a node to end of list
875 //
add_op(int op_num)876 void sexp_list_item::add_op(int op_num) {
877 sexp_list_item* item, * ptr;
878
879 item = new sexp_list_item;
880 ptr = this;
881 while (ptr->next) {
882 ptr = ptr->next;
883 }
884
885 ptr->next = item;
886 item->set_op(op_num);
887 }
888
889 // add a node to end of list
890 // Defaults: t = SEXPT_STRING
891 //
add_data(const char * str,int t)892 void sexp_list_item::add_data(const char* str, int t) {
893 sexp_list_item* item, * ptr;
894
895 item = new sexp_list_item;
896 ptr = this;
897 while (ptr->next) {
898 ptr = ptr->next;
899 }
900
901 ptr->next = item;
902 item->set_data(str, t);
903 }
904
905 // add a node to end of list, allocating memory for the text
906 // Defaults: t = SEXPT_STRING
907 //
add_data_dup(const char * str,int t)908 void sexp_list_item::add_data_dup(const char* str, int t) {
909 sexp_list_item* item, * ptr;
910
911 item = new sexp_list_item;
912 ptr = this;
913 while (ptr->next) {
914 ptr = ptr->next;
915 }
916
917 ptr->next = item;
918 item->set_data(strdup(str), t);
919 item->flags |= SEXP_ITEM_F_DUP;
920 }
921
922 // add an sexp list to end of another list (join lists)
923 //
add_list(sexp_list_item * list)924 void sexp_list_item::add_list(sexp_list_item* list) {
925 sexp_list_item* ptr;
926
927 ptr = this;
928 while (ptr->next) {
929 ptr = ptr->next;
930 }
931
932 ptr->next = list;
933 }
934
935 // free all nodes of list
936 //
destroy()937 void sexp_list_item::destroy() {
938 sexp_list_item* ptr, * ptr2;
939
940 ptr = this;
941 while (ptr) {
942 ptr2 = ptr->next;
943
944 delete ptr;
945 ptr = ptr2;
946 }
947 }
948
add_default_operator(int op_index,int argnum)949 int sexp_tree::add_default_operator(int op_index, int argnum) {
950 char buf[256];
951 int index;
952 sexp_list_item item;
953
954 index = item_index;
955 if (get_default_value(&item, buf, op_index, argnum)) {
956 return -1;
957 }
958
959 if (item.type & SEXPT_OPERATOR) {
960 Assert((item.op >= 0) && (item.op < (int) Operators.size()));
961 add_or_replace_operator(item.op);
962 item_index = index;
963 } else {
964 // special case for sexps that take variables
965 if (query_operator_argument_type(op_index, argnum) == OPF_VARIABLE_NAME) {
966 int sexp_var_index = get_index_sexp_variable_name(item.text);
967 Assert(sexp_var_index != -1);
968 int type = SEXPT_VALID | SEXPT_VARIABLE;
969 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
970 type |= SEXPT_STRING;
971 } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
972 type |= SEXPT_NUMBER;
973 } else {
974 Int3();
975 }
976
977 char node_text[2 * TOKEN_LENGTH + 2];
978 sprintf(node_text, "%s(%s)", item.text.c_str(), Sexp_variables[sexp_var_index].text);
979 add_variable_data(node_text, type);
980 }
981 // modify-variable data type depends on type of variable being modified
982 // (we know this block is handling the second argument since it's not OPF_VARIABLE_NAME)
983 else if (Operators[op_index].value == OP_MODIFY_VARIABLE) {
984 // the the variable name
985 char buf2[256];
986 Assert(argnum == 1);
987 sexp_list_item temp_item;
988 get_default_value(&temp_item, buf2, op_index, 0);
989 int sexp_var_index = get_index_sexp_variable_name(temp_item.text);
990 Assert(sexp_var_index != -1);
991
992 // from name get type
993 int temp_type = Sexp_variables[sexp_var_index].type;
994 int type = 0;
995 if (temp_type & SEXP_VARIABLE_NUMBER) {
996 type = SEXPT_VALID | SEXPT_NUMBER;
997 } else if (temp_type & SEXP_VARIABLE_STRING) {
998 type = SEXPT_VALID | SEXPT_STRING;
999 } else {
1000 Int3();
1001 }
1002 add_data(item.text.c_str(), type);
1003 }
1004 // all other sexps and parameters
1005 else {
1006 add_data(item.text.c_str(), item.type);
1007 }
1008 }
1009
1010 return 0;
1011 }
1012
get_default_value(sexp_list_item * item,char * text_buf,int op,int i)1013 int sexp_tree::get_default_value(sexp_list_item* item, char* text_buf, int op, int i) {
1014 const char* str = NULL;
1015 int type, index;
1016 sexp_list_item* list;
1017
1018 index = item_index;
1019 type = query_operator_argument_type(op, i);
1020 switch (type) {
1021 case OPF_NULL:
1022 item->set_op(OP_NOP);
1023 return 0;
1024
1025 case OPF_BOOL:
1026 item->set_op(OP_TRUE);
1027 return 0;
1028
1029 case OPF_ANYTHING:
1030 if (Operators[op].value == OP_INVALIDATE_ARGUMENT || Operators[op].value == OP_VALIDATE_ARGUMENT)
1031 item->set_data(SEXP_ARGUMENT_STRING); // this is almost always what you want for these sexps
1032 else
1033 item->set_data("<any data>");
1034 return 0;
1035
1036 case OPF_NUMBER:
1037 case OPF_POSITIVE:
1038 case OPF_AMBIGUOUS:
1039 // if the top level operators is an AI goal, and we are adding the last number required,
1040 // assume that this number is a priority and make it 89 instead of 1.
1041 if ((query_operator_return_type(op) == OPR_AI_GOAL) && (i == (Operators[op].min - 1))) {
1042 item->set_data("89", (SEXPT_NUMBER | SEXPT_VALID));
1043 } else if (((Operators[op].value == OP_HAS_DOCKED_DELAY) || (Operators[op].value == OP_HAS_UNDOCKED_DELAY)
1044 || (Operators[op].value == OP_TIME_DOCKED) || (Operators[op].value == OP_TIME_UNDOCKED)) && (i == 2)) {
1045 item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
1046 } else if ((Operators[op].value == OP_SHIP_TYPE_DESTROYED) || (Operators[op].value == OP_GOOD_SECONDARY_TIME)) {
1047 item->set_data("100", (SEXPT_NUMBER | SEXPT_VALID));
1048 } else if (Operators[op].value == OP_SET_SUPPORT_SHIP) {
1049 item->set_data("-1", (SEXPT_NUMBER | SEXPT_VALID));
1050 } else if (((Operators[op].value == OP_SHIP_TAG) && (i == 1))
1051 || ((Operators[op].value == OP_TRIGGER_SUBMODEL_ANIMATION) && (i == 3))) {
1052 item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
1053 } else if (Operators[op].value == OP_EXPLOSION_EFFECT) {
1054 int temp;
1055 char sexp_str_token[TOKEN_LENGTH];
1056
1057 switch (i) {
1058 case 3:
1059 temp = 10;
1060 break;
1061 case 4:
1062 temp = 10;
1063 break;
1064 case 5:
1065 temp = 100;
1066 break;
1067 case 6:
1068 temp = 10;
1069 break;
1070 case 7:
1071 temp = 100;
1072 break;
1073 case 11:
1074 temp = (int) EMP_DEFAULT_INTENSITY;
1075 break;
1076 case 12:
1077 temp = (int) EMP_DEFAULT_TIME;
1078 break;
1079 default:
1080 temp = 0;
1081 break;
1082 }
1083
1084 // Goober5000 - set_data_dup is required if we're passing a variable
1085 sprintf(sexp_str_token, "%d", temp);
1086 item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
1087 } else if (Operators[op].value == OP_WARP_EFFECT) {
1088 int temp;
1089 char sexp_str_token[TOKEN_LENGTH];
1090
1091 switch (i) {
1092 case 6:
1093 temp = 100;
1094 break;
1095 case 7:
1096 temp = 10;
1097 break;
1098 default:
1099 temp = 0;
1100 break;
1101 }
1102
1103 // Goober5000 - set_data_dup is required if we're passing a variable
1104 sprintf(sexp_str_token, "%d", temp);
1105 item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
1106 } else if (Operators[op].value == OP_CHANGE_BACKGROUND) {
1107 item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
1108 } else if (Operators[op].value == OP_ADD_BACKGROUND_BITMAP) {
1109 int temp = 0;
1110 char sexp_str_token[TOKEN_LENGTH];
1111
1112 switch (i) {
1113 case 4:
1114 case 5:
1115 temp = 100;
1116 break;
1117
1118 case 6:
1119 case 7:
1120 temp = 1;
1121 break;
1122 }
1123
1124 sprintf(sexp_str_token, "%d", temp);
1125 item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
1126 } else if (Operators[op].value == OP_ADD_SUN_BITMAP) {
1127 int temp = 0;
1128 char sexp_str_token[TOKEN_LENGTH];
1129
1130 if (i == 4) {
1131 temp = 100;
1132 }
1133
1134 sprintf(sexp_str_token, "%d", temp);
1135 item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
1136 } else if (Operators[op].value == OP_MISSION_SET_NEBULA) {
1137 if (i == 0) {
1138 item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
1139 } else {
1140 item->set_data("3000", (SEXPT_NUMBER | SEXPT_VALID));
1141 }
1142 } else if (Operators[op].value == OP_MODIFY_VARIABLE) {
1143 if (get_modify_variable_type(index) == OPF_NUMBER) {
1144 item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
1145 } else {
1146 item->set_data("<any data>", (SEXPT_STRING | SEXPT_VALID));
1147 }
1148 } else if (Operators[op].value == OP_MODIFY_VARIABLE_XSTR) {
1149 if (i == 1) {
1150 item->set_data("<any data>", (SEXPT_STRING | SEXPT_VALID));
1151 } else {
1152 item->set_data("-1", (SEXPT_NUMBER | SEXPT_VALID));
1153 }
1154 } else if (Operators[op].value == OP_SET_VARIABLE_BY_INDEX) {
1155 if (i == 0) {
1156 item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
1157 } else {
1158 item->set_data("<any data>", (SEXPT_STRING | SEXPT_VALID));
1159 }
1160 } else if (Operators[op].value == OP_JETTISON_CARGO_NEW) {
1161 item->set_data("25", (SEXPT_NUMBER | SEXPT_VALID));
1162 } else if (Operators[op].value == OP_TECH_ADD_INTEL_XSTR || Operators[op].value == OP_TECH_REMOVE_INTEL_XSTR) {
1163 item->set_data("-1", (SEXPT_NUMBER | SEXPT_VALID));
1164 } else {
1165 item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
1166 }
1167
1168 return 0;
1169
1170 // Goober5000 - special cases that used to be numbers but are now hybrids
1171 case OPF_GAME_SND:
1172 {
1173 gamesnd_id sound_index;
1174
1175 if (Operators[op].value == OP_EXPLOSION_EFFECT)
1176 {
1177 sound_index = GameSounds::SHIP_EXPLODE_1;
1178 }
1179 else if (Operators[op].value == OP_WARP_EFFECT)
1180 {
1181 sound_index = (i == 8) ? GameSounds::CAPITAL_WARP_IN : GameSounds::CAPITAL_WARP_OUT;
1182 }
1183
1184 if (sound_index.isValid())
1185 {
1186 game_snd* snd = gamesnd_get_game_sound(sound_index);
1187 if (can_construe_as_integer(snd->name.c_str()))
1188 item->set_data(snd->name.c_str(), (SEXPT_NUMBER | SEXPT_VALID));
1189 else
1190 item->set_data(snd->name.c_str(), (SEXPT_STRING | SEXPT_VALID));
1191 return 0;
1192 }
1193
1194 // if no hardcoded default, just use the listing default
1195 break;
1196 }
1197
1198 // Goober5000 - ditto
1199 case OPF_FIREBALL:
1200 {
1201 int fireball_index = -1;
1202
1203 if (Operators[op].value == OP_EXPLOSION_EFFECT)
1204 {
1205 fireball_index = FIREBALL_MEDIUM_EXPLOSION;
1206 }
1207 else if (Operators[op].value == OP_WARP_EFFECT)
1208 {
1209 fireball_index = FIREBALL_WARP;
1210 }
1211
1212 if (fireball_index >= 0)
1213 {
1214 char *unique_id = Fireball_info[fireball_index].unique_id;
1215 if (strlen(unique_id) > 0)
1216 item->set_data(unique_id, (SEXPT_STRING | SEXPT_VALID));
1217 else
1218 {
1219 char num_str[NAME_LENGTH];
1220 sprintf(num_str, "%d", fireball_index);
1221 item->set_data(num_str, (SEXPT_NUMBER | SEXPT_VALID));
1222 }
1223 return 0;
1224 }
1225
1226 // if no hardcoded default, just use the listing default
1227 break;
1228 }
1229
1230 // new default value
1231 case OPF_PRIORITY:
1232 item->set_data("Normal", (SEXPT_STRING | SEXPT_VALID));
1233 return 0;
1234 }
1235
1236 list = get_listing_opf(type, index, i);
1237
1238 // Goober5000 - the way this is done is really stupid, so stupid hacks are needed to deal with it
1239 // this particular hack is necessary because the argument string should never be a default
1240 if (list && list->text == SEXP_ARGUMENT_STRING) {
1241 sexp_list_item* first_ptr;
1242
1243 first_ptr = list;
1244 list = list->next;
1245
1246 delete first_ptr;
1247 }
1248
1249 if (list) {
1250 // copy the information from the list to the passed-in item
1251 *item = *list;
1252
1253 // but use the provided text buffer
1254 strcpy(text_buf, list->text.c_str());
1255 item->text = text_buf;
1256
1257 // get rid of the list, since we're done with it
1258 list->destroy();
1259 item->next = NULL;
1260
1261 return 0;
1262 }
1263
1264 // catch anything that doesn't have a default value. Just describe what should be here instead
1265 switch (type) {
1266 case OPF_SHIP:
1267 case OPF_SHIP_NOT_PLAYER:
1268 case OPF_SHIP_POINT:
1269 case OPF_SHIP_WING:
1270 case OPF_SHIP_WING_WHOLETEAM:
1271 case OPF_SHIP_WING_SHIPONTEAM_POINT:
1272 case OPF_SHIP_WING_POINT:
1273 str = "<name of ship here>";
1274 break;
1275
1276 case OPF_ORDER_RECIPIENT:
1277 str = "<all fighters>";
1278 break;
1279
1280 case OPF_SHIP_OR_NONE:
1281 case OPF_SUBSYSTEM_OR_NONE:
1282 case OPF_SHIP_WING_POINT_OR_NONE:
1283 str = SEXP_NONE_STRING;
1284 break;
1285
1286 case OPF_WING:
1287 str = "<name of wing here>";
1288 break;
1289
1290 case OPF_DOCKER_POINT:
1291 str = "<docker point>";
1292 break;
1293
1294 case OPF_DOCKEE_POINT:
1295 str = "<dockee point>";
1296 break;
1297
1298 case OPF_SUBSYSTEM:
1299 case OPF_AWACS_SUBSYSTEM:
1300 case OPF_ROTATING_SUBSYSTEM:
1301 case OPF_SUBSYS_OR_GENERIC:
1302 str = "<name of subsystem>";
1303 break;
1304
1305 case OPF_SUBSYSTEM_TYPE:
1306 str = Subsystem_types[SUBSYSTEM_NONE];
1307 break;
1308
1309 case OPF_POINT:
1310 str = "<waypoint>";
1311 break;
1312
1313 case OPF_MESSAGE:
1314 str = "<Message>";
1315 break;
1316
1317 case OPF_WHO_FROM:
1318 //str = "<any allied>";
1319 str = "<any wingman>";
1320 break;
1321
1322 case OPF_WAYPOINT_PATH:
1323 str = "<waypoint path>";
1324 break;
1325
1326 case OPF_MISSION_NAME:
1327 str = "<mission name>";
1328 break;
1329
1330 case OPF_GOAL_NAME:
1331 str = "<goal name>";
1332 break;
1333
1334 case OPF_SHIP_TYPE:
1335 str = "<ship type here>";
1336 break;
1337
1338 case OPF_EVENT_NAME:
1339 str = "<event name>";
1340 break;
1341
1342 case OPF_HUGE_WEAPON:
1343 str = "<huge weapon type>";
1344 break;
1345
1346 case OPF_JUMP_NODE_NAME:
1347 str = "<Jump node name>";
1348 break;
1349
1350 case OPF_NAV_POINT:
1351 str = "<Nav 1>";
1352 break;
1353
1354 case OPF_ANYTHING:
1355 str = "<any data>";
1356 break;
1357
1358 case OPF_PERSONA:
1359 str = "<persona name>";
1360 break;
1361
1362 case OPF_FONT:
1363 str = const_cast<char*>(font::FontManager::getFont(0)->getName().c_str());
1364 break;
1365
1366 case OPF_AUDIO_VOLUME_OPTION:
1367 str = "Music";
1368 break;
1369
1370 case OPF_POST_EFFECT:
1371 str = "<Effect Name>";
1372 break;
1373
1374 case OPF_CUSTOM_HUD_GAUGE:
1375 str = "<Custom hud gauge>";
1376 break;
1377
1378 default:
1379 str = "<new default required!>";
1380 break;
1381 }
1382
1383 item->set_data(str, (SEXPT_STRING | SEXPT_VALID));
1384 return 0;
1385 }
1386
query_default_argument_available(int op)1387 int sexp_tree::query_default_argument_available(int op) {
1388 int i;
1389
1390 Assert(op >= 0);
1391 for (i = 0; i < Operators[op].min; i++) {
1392 if (!query_default_argument_available(op, i)) {
1393 return 0;
1394 }
1395 }
1396
1397 return 1;
1398 }
1399
query_default_argument_available(int op,int i)1400 int sexp_tree::query_default_argument_available(int op, int i) {
1401 int j, type;
1402 object* ptr;
1403
1404 type = query_operator_argument_type(op, i);
1405 switch (type) {
1406 case OPF_NONE:
1407 case OPF_NULL:
1408 case OPF_BOOL:
1409 case OPF_NUMBER:
1410 case OPF_POSITIVE:
1411 case OPF_IFF:
1412 case OPF_AI_CLASS:
1413 case OPF_WHO_FROM:
1414 case OPF_PRIORITY:
1415 case OPF_SHIP_TYPE:
1416 case OPF_SUBSYSTEM:
1417 case OPF_AWACS_SUBSYSTEM:
1418 case OPF_ROTATING_SUBSYSTEM:
1419 case OPF_SUBSYSTEM_TYPE:
1420 case OPF_DOCKER_POINT:
1421 case OPF_DOCKEE_POINT:
1422 case OPF_AI_GOAL:
1423 case OPF_KEYPRESS:
1424 case OPF_AI_ORDER:
1425 case OPF_SKILL_LEVEL:
1426 case OPF_MEDAL_NAME:
1427 case OPF_WEAPON_NAME:
1428 case OPF_INTEL_NAME:
1429 case OPF_SHIP_CLASS_NAME:
1430 case OPF_HUGE_WEAPON:
1431 case OPF_JUMP_NODE_NAME:
1432 case OPF_AMBIGUOUS:
1433 case OPF_CARGO:
1434 case OPF_ARRIVAL_LOCATION:
1435 case OPF_DEPARTURE_LOCATION:
1436 case OPF_ARRIVAL_ANCHOR_ALL:
1437 case OPF_SUPPORT_SHIP_CLASS:
1438 case OPF_SHIP_WITH_BAY:
1439 case OPF_SOUNDTRACK_NAME:
1440 case OPF_STRING:
1441 case OPF_FLEXIBLE_ARGUMENT:
1442 case OPF_ANYTHING:
1443 case OPF_SKYBOX_MODEL_NAME:
1444 case OPF_SKYBOX_FLAGS:
1445 case OPF_SHIP_OR_NONE:
1446 case OPF_SUBSYSTEM_OR_NONE:
1447 case OPF_SHIP_WING_POINT_OR_NONE:
1448 case OPF_SUBSYS_OR_GENERIC:
1449 case OPF_BACKGROUND_BITMAP:
1450 case OPF_SUN_BITMAP:
1451 case OPF_NEBULA_STORM_TYPE:
1452 case OPF_NEBULA_POOF:
1453 case OPF_TURRET_TARGET_ORDER:
1454 case OPF_POST_EFFECT:
1455 case OPF_TARGET_PRIORITIES:
1456 case OPF_ARMOR_TYPE:
1457 case OPF_DAMAGE_TYPE:
1458 case OPF_FONT:
1459 case OPF_HUD_ELEMENT:
1460 case OPF_SOUND_ENVIRONMENT:
1461 case OPF_SOUND_ENVIRONMENT_OPTION:
1462 case OPF_EXPLOSION_OPTION:
1463 case OPF_AUDIO_VOLUME_OPTION:
1464 case OPF_WEAPON_BANK_NUMBER:
1465 case OPF_MESSAGE_OR_STRING:
1466 case OPF_BUILTIN_HUD_GAUGE:
1467 case OPF_CUSTOM_HUD_GAUGE:
1468 case OPF_SHIP_EFFECT:
1469 case OPF_ANIMATION_TYPE:
1470 case OPF_SHIP_FLAG:
1471 case OPF_NEBULA_PATTERN:
1472 case OPF_NAV_POINT:
1473 case OPF_TEAM_COLOR:
1474 case OPF_GAME_SND:
1475 case OPF_FIREBALL:
1476 case OPF_SPECIES:
1477 case OPF_LANGUAGE:
1478 return 1;
1479
1480 case OPF_SHIP:
1481 case OPF_SHIP_WING:
1482 case OPF_SHIP_POINT:
1483 case OPF_SHIP_WING_POINT:
1484 case OPF_SHIP_WING_WHOLETEAM:
1485 case OPF_SHIP_WING_SHIPONTEAM_POINT:
1486 ptr = GET_FIRST(&obj_used_list);
1487 while (ptr != END_OF_LIST(&obj_used_list)) {
1488 if (ptr->type == OBJ_SHIP || ptr->type == OBJ_START) {
1489 return 1;
1490 }
1491
1492 ptr = GET_NEXT(ptr);
1493 }
1494
1495 return 0;
1496
1497 case OPF_SHIP_NOT_PLAYER:
1498 case OPF_ORDER_RECIPIENT:
1499 ptr = GET_FIRST(&obj_used_list);
1500 while (ptr != END_OF_LIST(&obj_used_list)) {
1501 if (ptr->type == OBJ_SHIP) {
1502 return 1;
1503 }
1504
1505 ptr = GET_NEXT(ptr);
1506 }
1507
1508 return 0;
1509
1510 case OPF_WING:
1511 for (j = 0; j < MAX_WINGS; j++) {
1512 if (Wings[j].wave_count) {
1513 return 1;
1514 }
1515 }
1516
1517 return 0;
1518
1519 case OPF_PERSONA:
1520 return (Num_personas > 0) ? 1 : 0;
1521
1522 case OPF_POINT:
1523 case OPF_WAYPOINT_PATH:
1524 return Waypoint_lists.empty() ? 0 : 1;
1525
1526 case OPF_MISSION_NAME:
1527 return _interface->hasDefaultMissionName() ? 1 : 0;
1528
1529 // The following code is kept for when the campaign editor is implemented
1530 /*
1531 if (m_mode != MODE_CAMPAIGN) {
1532 if (!(*Mission_filename))
1533 return 0;
1534
1535 return 1;
1536 }
1537
1538 if (Campaign.num_missions > 0)
1539 return 1;
1540
1541 return 0;
1542 */
1543
1544 case OPF_GOAL_NAME: {
1545 return _interface->hasDefaultGoal(Operators[op].value) ? 1 : 0;
1546
1547 // The original code is kept until the campaign editor is implemented
1548 /*
1549 int value;
1550
1551 value = Operators[op].value;
1552
1553 if (m_mode == MODE_CAMPAIGN) {
1554 return 1;
1555
1556 // need to be sure that previous-goal functions are available. (i.e. we are providing a default argument for them)
1557 } else if ((value == OP_PREVIOUS_GOAL_TRUE) || (value == OP_PREVIOUS_GOAL_FALSE)
1558 || (value == OP_PREVIOUS_GOAL_INCOMPLETE) || Num_goals) {
1559 return 1;
1560 }
1561
1562 return 0;
1563 */
1564 }
1565
1566 case OPF_EVENT_NAME: {
1567 return _interface->hasDefaultEvent(Operators[op].value) ? 1 : 0;
1568
1569 // The original code is kept until the campaign editor is implemented
1570 /*
1571 int value;
1572
1573 value = Operators[op].value;
1574 if (m_mode == MODE_CAMPAIGN) {
1575 return 1;
1576
1577 // need to be sure that previous-event functions are available. (i.e. we are providing a default argument for them)
1578 } else if ((value == OP_PREVIOUS_EVENT_TRUE) || (value == OP_PREVIOUS_EVENT_FALSE)
1579 || (value == OP_PREVIOUS_EVENT_INCOMPLETE) || Num_mission_events) {
1580 return 1;
1581 }
1582
1583 return 0;
1584 */
1585 }
1586
1587 case OPF_MESSAGE:
1588 return _interface->hasDefaultMessageParamter() ? 1 : 0;
1589
1590 case OPF_VARIABLE_NAME:
1591 return (sexp_variable_count() > 0) ? 1 : 0;
1592
1593 case OPF_SSM_CLASS:
1594 return Ssm_info.empty() ? 0 : 1;
1595
1596 case OPF_MISSION_MOOD:
1597 return Builtin_moods.empty() ? 0 : 1;
1598
1599 default:
1600 Int3();
1601
1602 }
1603
1604 return 0;
1605 }
1606
1607 // expand a combined line (one with an operator and its one argument on the same line) into
1608 // 2 lines.
expand_operator(int node)1609 void sexp_tree::expand_operator(int node) {
1610 if (tree_nodes[node].flags & COMBINED) {
1611 node = tree_nodes[node].parent;
1612 Assert((tree_nodes[node].flags & OPERAND) && (tree_nodes[node].flags & EDITABLE));
1613 }
1614
1615 if ((tree_nodes[node].flags & OPERAND) && (tree_nodes[node].flags & EDITABLE)) { // expandable?
1616 Assert(tree_nodes[node].type & SEXPT_OPERATOR);
1617 auto h = tree_nodes[node].handle;
1618 auto child_data = tree_nodes[node].child;
1619 Assert(child_data != -1 && tree_nodes[child_data].next == -1 && tree_nodes[child_data].child == -1);
1620
1621 h->setText(0, QString::fromUtf8(tree_nodes[node].text));
1622 tree_nodes[node].flags = OPERAND;
1623 auto bmap = get_data_image(child_data);
1624 tree_nodes[child_data].handle = insert(tree_nodes[child_data].text, bmap, h);
1625 tree_nodes[child_data].flags = EDITABLE;
1626 h->setExpanded(true);
1627 }
1628 }
1629
1630 // expand a CTreeCtrl branch and all of its children
expand_branch(QTreeWidgetItem * h)1631 void sexp_tree::expand_branch(QTreeWidgetItem* h) {
1632 h->setExpanded(true);
1633 for (auto i = 0; i < h->childCount(); ++i) {
1634 expand_branch(h->child(i));
1635 }
1636 }
1637
merge_operator(int)1638 void sexp_tree::merge_operator(int /*node*/) {
1639 /* char buf[256];
1640 int child;
1641
1642 if (tree_nodes[node].flags == EDITABLE) // data
1643 node = tree_nodes[node].parent;
1644
1645 if (node != -1) {
1646 child = tree_nodes[node].child;
1647 if (child != -1 && tree_nodes[child].next == -1 && tree_nodes[child].child == -1) {
1648 sprintf(buf, "%s %s", tree_nodes[node].text, tree_nodes[child].text);
1649 SetItemText(tree_nodes[node].handle, buf);
1650 tree_nodes[node].flags = OPERAND | EDITABLE;
1651 tree_nodes[child].flags = COMBINED;
1652 DeleteItem(tree_nodes[child].handle);
1653 tree_nodes[child].handle = NULL;
1654 return;
1655 }
1656 }*/
1657 }
1658
1659 // add a data node under operator pointed to by item_index
add_data(const char * new_data,int type)1660 int sexp_tree::add_data(const char* new_data, int type) {
1661 int node;
1662
1663 expand_operator(item_index);
1664 node = allocate_node(item_index);
1665 set_node(node, type, new_data);
1666 auto bmap = get_data_image(node);
1667 tree_nodes[node].handle = insert(new_data, bmap, tree_nodes[item_index].handle);
1668 tree_nodes[node].flags = EDITABLE;
1669 modified();
1670 return node;
1671 }
1672
1673 // add a (variable) data node under operator pointed to by item_index
add_variable_data(const char * new_data,int type)1674 int sexp_tree::add_variable_data(const char* new_data, int type) {
1675 int node;
1676
1677 Assert(type & SEXPT_VARIABLE);
1678
1679 expand_operator(item_index);
1680 node = allocate_node(item_index);
1681 set_node(node, type, new_data);
1682 tree_nodes[node].handle = insert(new_data, NodeImage::VARIABLE, tree_nodes[item_index].handle);
1683 tree_nodes[node].handle->setFlags(tree_nodes[node].handle->flags().setFlag(Qt::ItemIsEditable, false));
1684 tree_nodes[node].flags = NOT_EDITABLE;
1685 modified();
1686 return node;
1687 }
1688
1689 // add an operator under operator pointed to by item_index. Updates item_index to point
1690 // to this new operator.
add_operator(const char * op,QTreeWidgetItem * h)1691 int sexp_tree::add_operator(const char* op, QTreeWidgetItem* h) {
1692 int node;
1693
1694 if (item_index == -1) {
1695 node = allocate_node(-1);
1696 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
1697 tree_nodes[node].handle = insert(op, NodeImage::OPERATOR, h);
1698 } else {
1699 expand_operator(item_index);
1700 node = allocate_node(item_index);
1701 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
1702 tree_nodes[node].handle = insert(op, NodeImage::OPERATOR, tree_nodes[item_index].handle);
1703 }
1704
1705 tree_nodes[node].flags = OPERAND;
1706 setCurrentItemIndex(node);
1707 modified();
1708
1709 return node;
1710 }
1711
1712 // add an operator with one argument under operator pointed to by item_index. This function
1713 // exists because the one arg case is a special case. The operator and argument is
1714 // displayed on the same line.
1715 /*void sexp_tree::add_one_arg_operator(char *op, char *data, int type)
1716 {
1717 char str[80];
1718 int node1, node2;
1719
1720 expand_operator(item_index);
1721 node1 = allocate_node(item_index);
1722 node2 = allocate_node(node1);
1723 set_node(node1, SEXPT_OPERATOR, op);
1724 set_node(node2, type, data);
1725 sprintf(str, "%s %s", op, data);
1726 tree_nodes[node1].handle = insert(str, tree_nodes[item_index].handle);
1727 tree_nodes[node1].flags = OPERAND | EDITABLE;
1728 tree_nodes[node2].flags = COMBINED;
1729 *modified = 1;
1730 }*/
1731
1732 /*
1733 int sexp_tree::verify_tree(int *bypass)
1734 {
1735 return verify_tree(0, bypass);
1736 }
1737
1738 // check the sexp tree for errors. Return -1 if error, or 0 if no errors. If an error
1739 // is found, item_index = node of error.
1740 int sexp_tree::verify_tree(int node, int *bypass)
1741 {
1742 int i, type, count, op, type2, op2, argnum = 0;
1743
1744 if (!total_nodes)
1745 return 0; // nothing to check
1746
1747 Assert(node >= 0 && node < tree_nodes.size());
1748 Assert(tree_nodes[node].type == SEXPT_OPERATOR);
1749
1750 op = get_operator_index(tree_nodes[node].text);
1751 if (op == -1)
1752 return node_error(node, "Unknown operator", bypass);
1753
1754 count = count_args(tree_nodes[node].child);
1755 if (count < Operators[op].min)
1756 return node_error(node, "Too few arguments for operator", bypass);
1757 if (count > Operators[op].max)
1758 return node_error(node, "Too many arguments for operator", bypass);
1759
1760 node = tree_nodes[node].child; // get first argument
1761 while (node != -1) {
1762 type = query_operator_argument_type(op, argnum);
1763 Assert(tree_nodes[node].type & SEXPT_VALID);
1764 if (tree_nodes[node].type == SEXPT_OPERATOR) {
1765 if (verify_tree(node) == -1)
1766 return -1;
1767
1768 op2 = get_operator_index(tree_nodes[node].text); // no error checking, because it was done in the call above.
1769 type2 = query_operator_return_type(op2);
1770
1771 } else if (tree_nodes[node].type == SEXPT_NUMBER) {
1772 char *ptr;
1773
1774 type2 = OPR_NUMBER;
1775 ptr = tree_nodes[node].text;
1776 while (*ptr)
1777 if (!isdigit(*ptr++))
1778 return node_error(node, "Number is invalid", bypass);
1779
1780 } else if (tree_nodes[node].type == SEXPT_STRING) {
1781 type2 = SEXP_ATOM_STRING;
1782
1783 } else
1784 Assert(0); // unknown and invalid sexp node type.
1785
1786 switch (type) {
1787 case OPF_NUMBER:
1788 if (type2 != OPR_NUMBER)
1789 return node_error(node, "Number or number return type expected here", bypass);
1790
1791 break;
1792
1793 case OPF_SHIP:
1794 if (type2 == SEXP_ATOM_STRING)
1795 if (ship_name_lookup(tree_nodes[node].text, 1) == -1)
1796 type2 = 0;
1797
1798 if (type2 != SEXP_ATOM_STRING)
1799 return node_error(node, "Ship name expected here", bypass);
1800
1801 break;
1802
1803 case OPF_WING:
1804 if (type2 == SEXP_ATOM_STRING)
1805 if (wing_name_lookup(tree_nodes[node].text) == -1)
1806 type2 = 0;
1807
1808 if (type2 != SEXP_ATOM_STRING)
1809 return node_error(node, "Wing name expected here", bypass);
1810
1811 break;
1812
1813 case OPF_SHIP_WING:
1814 if (type2 == SEXP_ATOM_STRING)
1815 if (ship_name_lookup(tree_nodes[node].text, 1) == -1)
1816 if (wing_name_lookup(tree_nodes[node].text) == -1)
1817 type2 = 0;
1818
1819 if (type2 != SEXP_ATOM_STRING)
1820 return node_error(node, "Ship or wing name expected here", bypass);
1821
1822 break;
1823
1824 case OPF_BOOL:
1825 if (type2 != OPR_BOOL)
1826 return node_error(node, "Boolean return type expected here", bypass);
1827
1828 break;
1829
1830 case OPF_NULL:
1831 if (type2 != OPR_NULL)
1832 return node_error(node, "No return type operator expected here", bypass);
1833
1834 break;
1835
1836 case OPF_POINT:
1837 if (type2 != SEXP_ATOM_STRING || verify_vector(tree_nodes[node].text))
1838 return node_error(node, "3d coordinate expected here", bypass);
1839
1840 break;
1841
1842 case OPF_SUBSYSTEM:
1843 case OPF_AWACS_SUBSYSTEM:
1844 case OPF_ROTATING_SUBSYSTEM:
1845 if (type2 == SEXP_ATOM_STRING)
1846 if (ai_get_subsystem_type(tree_nodes[node].text) == SUBSYSTEM_UNKNOWN)
1847 type2 = 0;
1848
1849 if (type2 != SEXP_ATOM_STRING)
1850 return node_error(node, "Subsystem name expected here", bypass);
1851
1852 break;
1853
1854 case OPF_IFF:
1855 if (type2 == SEXP_ATOM_STRING) {
1856 for (i=0; i<Num_iffs; i++)
1857 if (!stricmp(Team_names[i], tree_nodes[node].text))
1858 break;
1859 }
1860
1861 if (i == Num_iffs)
1862 return node_error(node, "Iff team type expected here", bypass);
1863
1864 break;
1865
1866 case OPF_AI_GOAL:
1867 if (type2 != OPR_AI_GOAL)
1868 return node_error(node, "Ai goal return type expected here", bypass);
1869
1870 break;
1871
1872 case OPF_FLEXIBLE_ARGUMENT:
1873 if (type2 != OPR_FLEXIBLE_ARGUMENT)
1874 return node_error(node, "Flexible argument return type expected here", bypass);
1875
1876 break;
1877
1878 case OPF_ANYTHING:
1879 break;
1880
1881 case OPF_DOCKER_POINT:
1882 if (type2 != SEXP_ATOM_STRING)
1883 return node_error(node, "Docker docking point name expected here", bypass);
1884
1885 break;
1886
1887 case OPF_DOCKEE_POINT:
1888 if (type2 != SEXP_ATOM_STRING)
1889 return node_error(node, "Dockee docking point name expected here", bypass);
1890
1891 break;
1892 }
1893
1894 node = tree_nodes[node].next;
1895 argnum++;
1896 }
1897
1898 return 0;
1899 }
1900 */
1901
1902 // display an error message and position to point of error (a node)
node_error(int node,const char * msg,int * bypass)1903 int sexp_tree::node_error(int node, const char* msg, int* bypass) {
1904 if (bypass) {
1905 *bypass = 1;
1906 }
1907
1908 item_index = node;
1909 auto item_handle = tree_nodes[node].handle;
1910 if (tree_nodes[node].flags & COMBINED) {
1911 item_handle = tree_nodes[tree_nodes[node].parent].handle;
1912 }
1913
1914 ensure_visible(node);
1915 item_handle->setSelected(true);
1916
1917 auto text = QString("%1\n\nContinue checking for more errors?").arg(msg);
1918
1919 if (QMessageBox::critical(this, "Sexp error", text, QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
1920 return -1;
1921 } else {
1922 return 0;
1923 }
1924 }
1925
hilite_item(int node)1926 void sexp_tree::hilite_item(int node) {
1927
1928 ensure_visible(node);
1929 clearSelection();
1930 setCurrentItem(tree_nodes[node].handle);
1931 scrollToItem(tree_nodes[node].handle);
1932 }
1933
1934 // because the MFC function EnsureVisible() doesn't do what it says it does, I wrote this.
ensure_visible(int node)1935 void sexp_tree::ensure_visible(int node) {
1936 auto handle = tree_nodes[node].handle->parent();
1937
1938 while (handle != nullptr) {
1939 handle->setExpanded(true);
1940 handle = handle->parent();
1941 }
1942 }
1943
get_variable_default_text_from_variable_text(char * text,char * default_text)1944 void get_variable_default_text_from_variable_text(char* text, char* default_text) {
1945 char* start;
1946
1947 // find '('
1948 start = strstr(text, "(");
1949 Assert(start);
1950 start++;
1951
1952 // get length and copy all but last char ")"
1953 auto len = strlen(start);
1954 strncpy(default_text, start, len - 1);
1955
1956 // add null termination
1957 default_text[len - 1] = '\0';
1958 }
1959
get_variable_name_from_sexp_tree_node_text(const char * text,char * var_name)1960 void get_variable_name_from_sexp_tree_node_text(const char* text, char* var_name) {
1961 auto length = strcspn(text, "(");
1962
1963 strncpy(var_name, text, length);
1964 var_name[length] = '\0';
1965 }
1966
get_modify_variable_type(int parent)1967 int sexp_tree::get_modify_variable_type(int parent) {
1968 int sexp_var_index = -1;
1969
1970 Assert(parent >= 0);
1971 int op_const = get_operator_const(tree_nodes[parent].text);
1972
1973 Assert(tree_nodes[parent].child >= 0);
1974 char* node_text = tree_nodes[tree_nodes[parent].child].text;
1975
1976 if (op_const == OP_MODIFY_VARIABLE) {
1977 sexp_var_index = get_tree_name_to_sexp_variable_index(node_text);
1978 Assert(sexp_var_index >= 0);
1979 } else if (op_const == OP_SET_VARIABLE_BY_INDEX) {
1980 if (can_construe_as_integer(node_text)) {
1981 sexp_var_index = atoi(node_text);
1982 Assert(sexp_var_index >= 0);
1983 } else if (strchr(node_text, '(') && strchr(node_text, ')')) {
1984 // the variable index is itself a variable!
1985 return OPF_AMBIGUOUS;
1986 }
1987 } else {
1988 Int3(); // should not be called otherwise
1989 }
1990
1991 if (sexp_var_index < 0 || Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_BLOCK
1992 || Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NOT_USED) {
1993 // assume number so that we can allow tree display of number operators
1994 return OPF_NUMBER;
1995 } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
1996 return OPF_NUMBER;
1997 } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
1998 return OPF_AMBIGUOUS;
1999 } else {
2000 Int3();
2001 return 0;
2002 }
2003 }
2004
2005
verify_and_fix_arguments(int node)2006 void sexp_tree::verify_and_fix_arguments(int node) {
2007 int op_index, arg_num, type, tmp;
2008 static int here_count = 0;
2009 sexp_list_item* list, * ptr;
2010 bool is_variable_arg = false;
2011
2012 if (here_count) {
2013 return;
2014 }
2015
2016 here_count++;
2017 op_index = get_operator_index(tree_nodes[node].text);
2018 if (op_index < 0) {
2019 return;
2020 }
2021
2022 tmp = item_index;
2023
2024 arg_num = 0;
2025 setCurrentItemIndex(tree_nodes[node].child);
2026 while (item_index >= 0) {
2027 // get listing of valid argument values for node item_index
2028 type = query_operator_argument_type(op_index, arg_num);
2029 // special case for modify-variable
2030 if (type == OPF_AMBIGUOUS) {
2031 is_variable_arg = true;
2032 type = get_modify_variable_type(node);
2033 }
2034 if (query_restricted_opf_range(type)) {
2035 list = get_listing_opf(type, node, arg_num);
2036 if (!list && (arg_num >= Operators[op_index].min)) {
2037 free_node(item_index, 1);
2038 setCurrentItemIndex(tmp);
2039 here_count--;
2040 return;
2041 }
2042
2043 if (list) {
2044 // get a pointer to tree_nodes[item_index].text for normal value
2045 // or default variable value if variable
2046 char* text_ptr;
2047 char default_variable_text[TOKEN_LENGTH];
2048 if (tree_nodes[item_index].type & SEXPT_VARIABLE) {
2049 // special case for SEXPs which can modify a variable
2050 if (type == OPF_VARIABLE_NAME) {
2051 // make text_ptr to start - before '('
2052 get_variable_name_from_sexp_tree_node_text(tree_nodes[item_index].text, default_variable_text);
2053 text_ptr = default_variable_text;
2054 } else {
2055 // only the type needs checking for variables. It's up the to the FREDder to ensure the value is valid
2056 get_variable_name_from_sexp_tree_node_text(tree_nodes[item_index].text, default_variable_text);
2057 int sexp_var_index = get_index_sexp_variable_name(default_variable_text);
2058 bool types_match = false;
2059 Assert(sexp_var_index != -1);
2060
2061 switch (type) {
2062 case OPF_NUMBER:
2063 case OPF_POSITIVE:
2064 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
2065 types_match = true;
2066 }
2067 break;
2068
2069 default:
2070 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
2071 types_match = true;
2072 }
2073 }
2074
2075 if (types_match) {
2076 // on to the next argument
2077 setCurrentItemIndex(tree_nodes[item_index].next);
2078 arg_num++;
2079 continue;
2080 } else {
2081 // shouldn't really be getting here unless someone has been hacking the mission in a text editor
2082 get_variable_default_text_from_variable_text(tree_nodes[item_index].text,
2083 default_variable_text);
2084 text_ptr = default_variable_text;
2085 }
2086 }
2087 } else {
2088 text_ptr = tree_nodes[item_index].text;
2089 }
2090
2091 ptr = list;
2092 while (ptr) {
2093 // make sure text is not NULL
2094 // check that proposed text is valid for operator
2095 if (!stricmp(ptr->text.c_str(), text_ptr)) {
2096 break;
2097 }
2098
2099 ptr = ptr->next;
2100 }
2101
2102 if (!ptr) { // argument isn't in list of valid choices,
2103 if (list->op >= 0) {
2104 replace_operator(list->text.c_str());
2105 } else {
2106 replace_data(list->text.c_str(), list->type);
2107 }
2108 }
2109
2110 } else {
2111 bool invalid = false;
2112 if (type == OPF_AMBIGUOUS) {
2113 if (SEXPT_TYPE(tree_nodes[item_index].type) == SEXPT_OPERATOR) {
2114 invalid = true;
2115 }
2116 } else {
2117 if (SEXPT_TYPE(tree_nodes[item_index].type) != SEXPT_OPERATOR) {
2118 invalid = true;
2119 }
2120 }
2121
2122 if (invalid) {
2123 replace_data("<Invalid>", (SEXPT_STRING | SEXPT_VALID));
2124 }
2125 }
2126
2127 if (tree_nodes[item_index].type & SEXPT_OPERATOR) {
2128 verify_and_fix_arguments(item_index);
2129 }
2130
2131 }
2132
2133 //fix the node if it is the argument for modify-variable
2134 if (is_variable_arg //&&
2135 // !(tree_nodes[item_index].type & SEXPT_OPERATOR || tree_nodes[item_index].type & SEXPT_VARIABLE )
2136 ) {
2137 switch (type) {
2138 case OPF_AMBIGUOUS:
2139 tree_nodes[item_index].type |= SEXPT_STRING;
2140 tree_nodes[item_index].type &= ~SEXPT_NUMBER;
2141 break;
2142
2143 case OPF_NUMBER:
2144 tree_nodes[item_index].type |= SEXPT_NUMBER;
2145 tree_nodes[item_index].type &= ~SEXPT_STRING;
2146 break;
2147
2148 default:
2149 Int3();
2150 }
2151 }
2152
2153 setCurrentItemIndex(tree_nodes[item_index].next);
2154 arg_num++;
2155 }
2156
2157 setCurrentItemIndex(tmp);
2158 here_count--;
2159 }
2160
replace_data(const char * new_data,int type)2161 void sexp_tree::replace_data(const char* new_data, int type) {
2162 auto node = tree_nodes[item_index].child;
2163 if (node != -1) {
2164 free_node2(node);
2165 }
2166
2167 tree_nodes[item_index].child = -1;
2168 auto h = tree_nodes[item_index].handle;
2169 while (h->childCount() > 0) {
2170 h->removeChild(h->child(0));
2171 }
2172
2173 set_node(item_index, type, new_data);
2174 h->setText(0, new_data);
2175 auto bmap = get_data_image(item_index);
2176 h->setIcon(0, convertNodeImageToIcon(bmap));
2177 h->setFlags(h->flags().setFlag(Qt::ItemIsEditable, true));
2178 tree_nodes[item_index].flags = EDITABLE;
2179
2180 // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
2181 verify_and_fix_arguments(tree_nodes[item_index].parent);
2182
2183 modified();
2184 update_help(currentItem());
2185 }
2186
2187
2188 // Replaces data with sexp_variable type data
replace_variable_data(int var_idx,int type)2189 void sexp_tree::replace_variable_data(int var_idx, int type) {
2190 char buf[128];
2191
2192 Assert(type & SEXPT_VARIABLE);
2193
2194 auto node = tree_nodes[item_index].child;
2195 if (node != -1) {
2196 free_node2(node);
2197 }
2198
2199 tree_nodes[item_index].child = -1;
2200 auto h = tree_nodes[item_index].handle;
2201 while (h->childCount() > 0) {
2202 h->removeChild(h->child(0));
2203 }
2204
2205 // Assemble name
2206 sprintf(buf, "%s(%s)", Sexp_variables[var_idx].variable_name, Sexp_variables[var_idx].text);
2207
2208 set_node(item_index, type, buf);
2209 h->setText(0, QString::fromUtf8(buf));
2210 h->setIcon(0, convertNodeImageToIcon(NodeImage::VARIABLE));
2211 h->setFlags(h->flags().setFlag(Qt::ItemIsEditable, false));
2212 tree_nodes[item_index].flags = NOT_EDITABLE;
2213
2214 // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
2215 verify_and_fix_arguments(tree_nodes[item_index].parent);
2216
2217 modified();
2218 update_help(currentItem());
2219 }
2220
2221
replace_operator(const char * op)2222 void sexp_tree::replace_operator(const char* op) {
2223 auto node = tree_nodes[item_index].child;
2224 if (node != -1) {
2225 free_node2(node);
2226 }
2227
2228 tree_nodes[item_index].child = -1;
2229 auto h = tree_nodes[item_index].handle;
2230 while (h->childCount() > 0) {
2231 h->removeChild(h->child(0));
2232 }
2233
2234 set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), op);
2235 h->setText(0, op);
2236 tree_nodes[item_index].flags = OPERAND;
2237 modified();
2238 update_help(currentItem());
2239
2240 // hack added at Allender's request. If changing ship in an ai-dock operator, re-default
2241 // docking point.
2242 }
2243
2244 /*void sexp_tree::replace_one_arg_operator(char *op, char *data, int type)
2245 {
2246 char str[80];
2247 int node;
2248 HTREEITEM h;
2249
2250 node = tree_nodes[item_index].child;
2251 if (node != -1)
2252 free_node2(node);
2253
2254 tree_nodes[item_index].child = -1;
2255 h = tree_nodes[item_index].handle;
2256 while (ItemHasChildren(h))
2257 DeleteItem(GetChildItem(h));
2258
2259 node = allocate_node(item_index);
2260 set_node(item_index, SEXPT_OPERATOR, op);
2261 set_node(node, type, data);
2262 sprintf(str, "%s %s", op, data);
2263 SetItemText(h, str);
2264 tree_nodes[item_index].flags = OPERAND | EDITABLE;
2265 tree_nodes[node].flags = COMBINED;
2266 *modified = 1;
2267 update_help(GetSelectedItem());
2268 }*/
2269
2270 // moves a whole sexp tree branch to a new position under 'parent' and after 'after'.
2271 // The expansion state is preserved, and node handles are updated.
move_branch(int source,int parent)2272 void sexp_tree::move_branch(int source, int parent) {
2273 int node;
2274
2275 // if no source, skip everything
2276 if (source != -1) {
2277 node = tree_nodes[source].parent;
2278 if (node != -1) {
2279 if (tree_nodes[node].child == source) {
2280 tree_nodes[node].child = tree_nodes[source].next;
2281 } else {
2282 node = tree_nodes[node].child;
2283 while (tree_nodes[node].next != source) {
2284 node = tree_nodes[node].next;
2285 Assert(node != -1);
2286 }
2287
2288 tree_nodes[node].next = tree_nodes[source].next;
2289 }
2290 }
2291
2292 tree_nodes[source].parent = parent;
2293 tree_nodes[source].next = -1;
2294 if (parent) {
2295 if (tree_nodes[parent].child == -1) {
2296 tree_nodes[parent].child = source;
2297 } else {
2298 node = tree_nodes[parent].child;
2299 while (tree_nodes[node].next != -1) {
2300 node = tree_nodes[node].next;
2301 }
2302
2303 tree_nodes[node].next = source;
2304 }
2305
2306 move_branch(tree_nodes[source].handle, tree_nodes[parent].handle);
2307
2308 } else {
2309 move_branch(tree_nodes[source].handle);
2310 }
2311 }
2312 }
2313
move_branch(QTreeWidgetItem * source,QTreeWidgetItem * parent,QTreeWidgetItem * after)2314 QTreeWidgetItem* sexp_tree::move_branch(QTreeWidgetItem* source, QTreeWidgetItem* parent, QTreeWidgetItem* after) {
2315 QTreeWidgetItem* h = nullptr;
2316 if (source) {
2317 uint i;
2318
2319 for (i = 0; i < tree_nodes.size(); i++) {
2320 if (tree_nodes[i].handle == source) {
2321 break;
2322 }
2323 }
2324
2325 if (i < tree_nodes.size()) {
2326 auto icon = source->icon(0);
2327 h = insertWithIcon(source->text(0), icon, parent, after);
2328 tree_nodes[i].handle = h;
2329 } else {
2330 auto icon = source->icon(0);
2331 h = insertWithIcon(source->text(0), icon, parent, after);
2332 }
2333
2334 h->setData(0, FormulaDataRole, source->data(0, FormulaDataRole));
2335 for (auto childIdx = 0; childIdx < source->childCount(); ++i) {
2336 auto child = source->child(childIdx);
2337
2338 move_branch(child, h);
2339 }
2340
2341 h->setExpanded(source->isExpanded());
2342
2343 source->parent()->removeChild(source);
2344 }
2345
2346 return h;
2347 }
2348
copy_branch(QTreeWidgetItem * source,QTreeWidgetItem * parent,QTreeWidgetItem * after)2349 void sexp_tree::copy_branch(QTreeWidgetItem* source, QTreeWidgetItem* parent, QTreeWidgetItem* after) {
2350 QTreeWidgetItem* h = nullptr;
2351 if (source) {
2352 uint i;
2353
2354 for (i = 0; i < tree_nodes.size(); i++) {
2355 if (tree_nodes[i].handle == source) {
2356 break;
2357 }
2358 }
2359
2360 if (i < tree_nodes.size()) {
2361 auto icon = source->icon(0);
2362 h = insertWithIcon(source->text(0), icon, parent, after);
2363 tree_nodes[i].handle = h;
2364 } else {
2365 auto icon = source->icon(0);
2366 h = insertWithIcon(source->text(0), icon, parent, after);
2367 }
2368
2369 h->setData(0, FormulaDataRole, source->data(0, FormulaDataRole));
2370 for (auto childIdx = 0; childIdx < source->childCount(); ++i) {
2371 auto child = source->child(childIdx);
2372
2373 move_branch(child, h);
2374 }
2375
2376 h->setExpanded(source->isExpanded());
2377 }
2378 }
2379
swap_roots(QTreeWidgetItem * one,QTreeWidgetItem * two)2380 void sexp_tree::swap_roots(QTreeWidgetItem* one, QTreeWidgetItem* two) {
2381 // copy_branch(one, TVI_ROOT, two);
2382 // move_branch(two, TVI_ROOT, one);
2383 // DeleteItem(one);
2384 auto h = move_branch(one, itemFromIndex(rootIndex()), two);
2385 setCurrentItem(h);
2386 modified();
2387 }
2388
2389 QTreeWidgetItem*
insert(const QString & lpszItem,NodeImage image,QTreeWidgetItem * hParent,QTreeWidgetItem * hInsertAfter)2390 sexp_tree::insert(const QString& lpszItem, NodeImage image, QTreeWidgetItem* hParent, QTreeWidgetItem* hInsertAfter) {
2391 return insertWithIcon(lpszItem, convertNodeImageToIcon(image), hParent, hInsertAfter);
2392 }
2393
insertWithIcon(const QString & lpszItem,const QIcon & image,QTreeWidgetItem * hParent,QTreeWidgetItem * hInsertAfter)2394 QTreeWidgetItem* sexp_tree::insertWithIcon(const QString& lpszItem,
2395 const QIcon& image,
2396 QTreeWidgetItem* hParent,
2397 QTreeWidgetItem* hInsertAfter) {
2398 util::SignalBlockers blockers(this);
2399
2400 QTreeWidgetItem* item = nullptr;
2401 if (hParent == nullptr) {
2402 if (hInsertAfter == nullptr) {
2403 item = new QTreeWidgetItem(this);
2404 } else {
2405 item = new QTreeWidgetItem(this, hInsertAfter);
2406 }
2407 } else {
2408 if (hInsertAfter == nullptr) {
2409 item = new QTreeWidgetItem(hParent);
2410 } else {
2411 item = new QTreeWidgetItem(hParent, hInsertAfter);
2412 }
2413 }
2414 item->setText(0, lpszItem);
2415 item->setIcon(0, image);
2416 item->setFlags(item->flags() | Qt::ItemIsEditable);
2417
2418 return item;
2419 }
2420
handle(int node)2421 QTreeWidgetItem* sexp_tree::handle(int node) {
2422 return tree_nodes[node].handle;
2423 }
2424
help(int code)2425 const char* sexp_tree::help(int code) {
2426 int i;
2427
2428 i = (int) Sexp_help.size();
2429 while (i--) {
2430 if (Sexp_help[i].id == code) {
2431 break;
2432 }
2433 }
2434
2435 if (i >= 0) {
2436 return Sexp_help[i].help.c_str();
2437 }
2438
2439 return NULL;
2440 }
2441
2442 // get type of item clicked on
get_type(QTreeWidgetItem * h)2443 int sexp_tree::get_type(QTreeWidgetItem* h) {
2444 uint i;
2445
2446 // get index into sexp_tree
2447 for (i = 0; i < tree_nodes.size(); i++) {
2448 if (tree_nodes[i].handle == h) {
2449 break;
2450 }
2451 }
2452
2453 if ((i >= tree_nodes.size())) {
2454 // Int3(); // This would be the root of the tree -- ie, event name
2455 return -1;
2456 }
2457
2458 return tree_nodes[i].type;
2459 }
2460
2461 // get node of item clicked on
get_node(QTreeWidgetItem * h)2462 int sexp_tree::get_node(QTreeWidgetItem* h) {
2463 uint i;
2464
2465 // get index into sexp_tree
2466 for (i = 0; i < tree_nodes.size(); i++) {
2467 if (tree_nodes[i].handle == h) {
2468 break;
2469 }
2470 }
2471
2472 if ((i >= tree_nodes.size())) {
2473 // Int3(); // This would be the root of the tree -- ie, event name
2474 return -1;
2475 }
2476
2477 return i;
2478 }
2479
update_help(QTreeWidgetItem * h)2480 void sexp_tree::update_help(QTreeWidgetItem* h) {
2481 if (h == nullptr) {
2482 helpChanged("");
2483 miniHelpChanged("");
2484 }
2485
2486 int i, j, z, c, code, index, sibling_place;
2487
2488 for (i = 0; i < (int) Operators.size(); i++) {
2489 for (j = 0; j < (int) op_menu.size(); j++) {
2490 if (get_category(Operators[i].value) == op_menu[j].id) {
2491 if (!help(Operators[i].value)) {
2492 mprintf(("Allender! If you add new sexp operators, add help for them too! :)\n"));
2493 }
2494 }
2495 }
2496 }
2497
2498 for (i = 0; i < (int) tree_nodes.size(); i++) {
2499 if (tree_nodes[i].handle == h) {
2500 break;
2501 }
2502 }
2503
2504 if ((i >= (int) tree_nodes.size()) || !tree_nodes[i].type) {
2505 helpChanged("");
2506 miniHelpChanged("");
2507 return;
2508 }
2509
2510 if (SEXPT_TYPE(tree_nodes[i].type) == SEXPT_OPERATOR) {
2511 miniHelpChanged("");
2512 } else {
2513 z = tree_nodes[i].parent;
2514 if (z < 0) {
2515 Warning(LOCATION, "Sexp data \"%s\" has no parent!", tree_nodes[i].text);
2516 return;
2517 }
2518
2519 code = get_operator_const(tree_nodes[z].text);
2520 index = get_operator_index(tree_nodes[z].text);
2521 sibling_place = get_sibling_place(i) + 1; //We want it to start at 1
2522
2523 //*****Minihelp box
2524 if ((SEXPT_TYPE(tree_nodes[i].type) == SEXPT_NUMBER)
2525 || ((SEXPT_TYPE(tree_nodes[i].type) == SEXPT_STRING) && sibling_place > 0)) {
2526 char buffer[10240] = { "" };
2527
2528 //Get the help for the current operator
2529 const char* helpstr = help(code);
2530 bool display_number = true;
2531
2532 //If a help string exists, try to display it
2533 if (helpstr != NULL) {
2534 char searchstr[32];
2535 const char* loc = NULL, * loc2 = NULL;
2536
2537 if (loc == NULL) {
2538 sprintf(searchstr, "\n%d:", sibling_place);
2539 loc = strstr(helpstr, searchstr);
2540 }
2541
2542 if (loc == NULL) {
2543 sprintf(searchstr, "\t%d:", sibling_place);
2544 loc = strstr(helpstr, searchstr);
2545 }
2546 if (loc == NULL) {
2547 sprintf(searchstr, " %d:", sibling_place);
2548 loc = strstr(helpstr, searchstr);
2549 }
2550 if (loc == NULL) {
2551 sprintf(searchstr, "%d:", sibling_place);
2552 loc = strstr(helpstr, searchstr);
2553 }
2554 if (loc == NULL) {
2555 loc = strstr(helpstr, "Rest:");
2556 }
2557 if (loc == NULL) {
2558 loc = strstr(helpstr, "All:");
2559 }
2560
2561 if (loc != NULL) {
2562 //Skip whitespace
2563 while (*loc == '\r' || *loc == '\n' || *loc == ' ' || *loc == '\t') {
2564 loc++;
2565 }
2566
2567 //Find EOL
2568 loc2 = strpbrk(loc, "\r\n");
2569 if (loc2 != NULL) {
2570 size_t size = loc2 - loc;
2571 strncpy(buffer, loc, size);
2572 if (size < sizeof(buffer)) {
2573 buffer[size] = '\0';
2574 }
2575 display_number = false;
2576 } else {
2577 strcpy_s(buffer, loc);
2578 display_number = false;
2579 }
2580 }
2581 }
2582
2583 //Display argument number
2584 if (display_number) {
2585 sprintf(buffer, "%d:", sibling_place);
2586 }
2587
2588 miniHelpChanged(QString::fromUtf8(buffer));
2589 }
2590
2591 if (index >= 0) {
2592 c = 0;
2593 j = tree_nodes[z].child;
2594 while ((j >= 0) && (j != i)) {
2595 j = tree_nodes[j].next;
2596 c++;
2597 }
2598
2599 Assert(j >= 0);
2600 if (query_operator_argument_type(index, c) == OPF_MESSAGE) {
2601 for (j = 0; j < Num_messages; j++) {
2602 if (!stricmp(Messages[j].name, tree_nodes[i].text)) {
2603 auto text = QString("Message Text:\n%1").arg(Messages[j].message);
2604 helpChanged(text);
2605 return;
2606 }
2607 }
2608 }
2609 }
2610
2611 i = z;
2612 }
2613
2614 code = get_operator_const(tree_nodes[i].text);
2615 auto str = help(code);
2616 if (!str) {
2617 str = "No help available";
2618 }
2619
2620 helpChanged(QString::fromUtf8(str));
2621 }
2622
2623 // find list of sexp_tree nodes with text
2624 // stuff node indices into find[]
find_text(const char * text,int * find,int max_depth)2625 int sexp_tree::find_text(const char* text, int* find, int max_depth) {
2626 int find_count;
2627
2628 // initialize find
2629 for (int i = 0; i < max_depth; i++) {
2630 find[i] = -1;
2631 }
2632
2633 find_count = 0;
2634
2635 for (size_t i = 0; i < tree_nodes.size(); i++) {
2636 // only look at used and editable nodes
2637 if ((tree_nodes[i].flags & EDITABLE && (tree_nodes[i].type != SEXPT_UNUSED))) {
2638 // find the text
2639 if (!stricmp(tree_nodes[i].text, text)) {
2640 find[find_count++] = static_cast<int>(i);
2641
2642 // don't exceed max count - array bounds
2643 if (find_count == max_depth) {
2644 break;
2645 }
2646 }
2647 }
2648 }
2649
2650 return find_count;
2651 }
2652
2653
2654 // Determine if a given opf code has a restricted argument range (i.e. has a specific, limited
2655 // set of argument values, or has virtually unlimited possibilities. For example, boolean values
2656 // only have true or false, so it is restricted, but a number could be anything, so it's not.
2657 //
query_restricted_opf_range(int opf)2658 int sexp_tree::query_restricted_opf_range(int opf) {
2659 switch (opf) {
2660 case OPF_NUMBER:
2661 case OPF_POSITIVE:
2662 case OPF_WHO_FROM:
2663
2664 // Goober5000 - these are needed too (otherwise the arguments revert to their defaults)
2665 case OPF_STRING:
2666 case OPF_ANYTHING:
2667 return 0;
2668 }
2669
2670 return 1;
2671 }
2672
2673 // generate listing of valid argument values.
2674 // opf = operator format to generate list for
2675 // parent_node = the parent node we are generating list for
2676 // arg_index = argument number of parent this argument will go at
2677 //
2678 // Goober5000 - add the listing from get_listing_opf_sub to the end of a new list containing
2679 // the special argument item, but only if it's a child of a when-argument (or similar) sexp.
2680 // Also only do this if the list has at least one item, because otherwise the argument code
2681 // would have nothing to select from.
get_listing_opf(int opf,int parent_node,int arg_index)2682 sexp_list_item* sexp_tree::get_listing_opf(int opf, int parent_node, int arg_index) {
2683 sexp_list_item head;
2684 sexp_list_item* list = NULL;
2685 int w_arg, e_arg;
2686
2687 switch (opf) {
2688 case OPF_NONE:
2689 list = NULL;
2690 break;
2691
2692 case OPF_NULL:
2693 list = get_listing_opf_null();
2694 break;
2695
2696 case OPF_BOOL:
2697 list = get_listing_opf_bool(parent_node);
2698 break;
2699
2700 case OPF_NUMBER:
2701 list = get_listing_opf_number();
2702 break;
2703
2704 case OPF_SHIP:
2705 list = get_listing_opf_ship(parent_node);
2706 break;
2707
2708 case OPF_WING:
2709 list = get_listing_opf_wing();
2710 break;
2711
2712 case OPF_AWACS_SUBSYSTEM:
2713 case OPF_ROTATING_SUBSYSTEM:
2714 case OPF_SUBSYSTEM:
2715 list = get_listing_opf_subsystem(parent_node, arg_index);
2716 break;
2717
2718 case OPF_SUBSYSTEM_TYPE:
2719 list = get_listing_opf_subsystem_type(parent_node);
2720 break;
2721
2722 case OPF_POINT:
2723 list = get_listing_opf_point();
2724 break;
2725
2726 case OPF_IFF:
2727 list = get_listing_opf_iff();
2728 break;
2729
2730 case OPF_AI_CLASS:
2731 list = get_listing_opf_ai_class();
2732 break;
2733
2734 case OPF_SUPPORT_SHIP_CLASS:
2735 list = get_listing_opf_support_ship_class();
2736 break;
2737
2738 case OPF_SSM_CLASS:
2739 list = get_listing_opf_ssm_class();
2740 break;
2741
2742 case OPF_ARRIVAL_LOCATION:
2743 list = get_listing_opf_arrival_location();
2744 break;
2745
2746 case OPF_DEPARTURE_LOCATION:
2747 list = get_listing_opf_departure_location();
2748 break;
2749
2750 case OPF_ARRIVAL_ANCHOR_ALL:
2751 list = get_listing_opf_arrival_anchor_all();
2752 break;
2753
2754 case OPF_SHIP_WITH_BAY:
2755 list = get_listing_opf_ship_with_bay();
2756 break;
2757
2758 case OPF_SOUNDTRACK_NAME:
2759 list = get_listing_opf_soundtrack_name();
2760 break;
2761
2762 case OPF_AI_GOAL:
2763 list = get_listing_opf_ai_goal(parent_node);
2764 break;
2765
2766 case OPF_FLEXIBLE_ARGUMENT:
2767 list = get_listing_opf_flexible_argument();
2768 break;
2769
2770 case OPF_DOCKER_POINT:
2771 list = get_listing_opf_docker_point(parent_node);
2772 break;
2773
2774 case OPF_DOCKEE_POINT:
2775 list = get_listing_opf_dockee_point(parent_node);
2776 break;
2777
2778 case OPF_MESSAGE:
2779 list = get_listing_opf_message();
2780 break;
2781
2782 case OPF_WHO_FROM:
2783 list = get_listing_opf_who_from();
2784 break;
2785
2786 case OPF_PRIORITY:
2787 list = get_listing_opf_priority();
2788 break;
2789
2790 case OPF_WAYPOINT_PATH:
2791 list = get_listing_opf_waypoint_path();
2792 break;
2793
2794 case OPF_POSITIVE:
2795 list = get_listing_opf_positive();
2796 break;
2797
2798 case OPF_MISSION_NAME:
2799 list = get_listing_opf_mission_name();
2800 break;
2801
2802 case OPF_SHIP_POINT:
2803 list = get_listing_opf_ship_point();
2804 break;
2805
2806 case OPF_GOAL_NAME:
2807 list = get_listing_opf_goal_name(parent_node);
2808 break;
2809
2810 case OPF_SHIP_WING:
2811 list = get_listing_opf_ship_wing();
2812 break;
2813
2814 case OPF_SHIP_WING_WHOLETEAM:
2815 list = get_listing_opf_ship_wing_wholeteam();
2816 break;
2817
2818 case OPF_SHIP_WING_SHIPONTEAM_POINT:
2819 list = get_listing_opf_ship_wing_shiponteam_point();
2820 break;
2821
2822 case OPF_SHIP_WING_POINT:
2823 list = get_listing_opf_ship_wing_point();
2824 break;
2825
2826 case OPF_SHIP_WING_POINT_OR_NONE:
2827 list = get_listing_opf_ship_wing_point_or_none();
2828 break;
2829
2830 case OPF_ORDER_RECIPIENT:
2831 list = get_listing_opf_order_recipient();
2832 break;
2833
2834 case OPF_SHIP_TYPE:
2835 list = get_listing_opf_ship_type();
2836 break;
2837
2838 case OPF_KEYPRESS:
2839 list = get_listing_opf_keypress();
2840 break;
2841
2842 case OPF_EVENT_NAME:
2843 list = get_listing_opf_event_name(parent_node);
2844 break;
2845
2846 case OPF_AI_ORDER:
2847 list = get_listing_opf_ai_order();
2848 break;
2849
2850 case OPF_SKILL_LEVEL:
2851 list = get_listing_opf_skill_level();
2852 break;
2853
2854 case OPF_CARGO:
2855 list = get_listing_opf_cargo();
2856 break;
2857
2858 case OPF_STRING:
2859 list = get_listing_opf_string();
2860 break;
2861
2862 case OPF_MEDAL_NAME:
2863 list = get_listing_opf_medal_name();
2864 break;
2865
2866 case OPF_WEAPON_NAME:
2867 list = get_listing_opf_weapon_name();
2868 break;
2869
2870 case OPF_INTEL_NAME:
2871 list = get_listing_opf_intel_name();
2872 break;
2873
2874 case OPF_SHIP_CLASS_NAME:
2875 list = get_listing_opf_ship_class_name();
2876 break;
2877
2878 case OPF_HUGE_WEAPON:
2879 list = get_listing_opf_huge_weapon();
2880 break;
2881
2882 case OPF_SHIP_NOT_PLAYER:
2883 list = get_listing_opf_ship_not_player();
2884 break;
2885
2886 case OPF_SHIP_OR_NONE:
2887 list = get_listing_opf_ship_or_none();
2888 break;
2889
2890 case OPF_SUBSYSTEM_OR_NONE:
2891 list = get_listing_opf_subsystem_or_none(parent_node, arg_index);
2892 break;
2893
2894 case OPF_SUBSYS_OR_GENERIC:
2895 list = get_listing_opf_subsys_or_generic(parent_node, arg_index);
2896 break;
2897
2898 case OPF_JUMP_NODE_NAME:
2899 list = get_listing_opf_jump_nodes();
2900 break;
2901
2902 case OPF_VARIABLE_NAME:
2903 list = get_listing_opf_variable_names();
2904 break;
2905
2906 case OPF_AMBIGUOUS:
2907 list = NULL;
2908 break;
2909
2910 case OPF_ANYTHING:
2911 list = NULL;
2912 break;
2913
2914 case OPF_SKYBOX_MODEL_NAME:
2915 list = get_listing_opf_skybox_model();
2916 break;
2917
2918 case OPF_SKYBOX_FLAGS:
2919 list = get_listing_opf_skybox_flags();
2920 break;
2921
2922 case OPF_BACKGROUND_BITMAP:
2923 list = get_listing_opf_background_bitmap();
2924 break;
2925
2926 case OPF_SUN_BITMAP:
2927 list = get_listing_opf_sun_bitmap();
2928 break;
2929
2930 case OPF_NEBULA_STORM_TYPE:
2931 list = get_listing_opf_nebula_storm_type();
2932 break;
2933
2934 case OPF_NEBULA_POOF:
2935 list = get_listing_opf_nebula_poof();
2936 break;
2937
2938 case OPF_TURRET_TARGET_ORDER:
2939 list = get_listing_opf_turret_target_order();
2940 break;
2941
2942 case OPF_TARGET_PRIORITIES:
2943 list = get_listing_opf_turret_target_priorities();
2944 break;
2945
2946 case OPF_ARMOR_TYPE:
2947 list = get_listing_opf_armor_type();
2948 break;
2949
2950 case OPF_DAMAGE_TYPE:
2951 list = get_listing_opf_damage_type();
2952 break;
2953
2954 case OPF_ANIMATION_TYPE:
2955 list = get_listing_opf_animation_type();
2956 break;
2957
2958 case OPF_PERSONA:
2959 list = get_listing_opf_persona();
2960 break;
2961
2962 case OPF_POST_EFFECT:
2963 list = get_listing_opf_post_effect();
2964 break;
2965
2966 case OPF_FONT:
2967 list = get_listing_opf_font();
2968 break;
2969
2970 case OPF_HUD_ELEMENT:
2971 list = get_listing_opf_hud_elements();
2972 break;
2973
2974 case OPF_SOUND_ENVIRONMENT:
2975 list = get_listing_opf_sound_environment();
2976 break;
2977
2978 case OPF_SOUND_ENVIRONMENT_OPTION:
2979 list = get_listing_opf_sound_environment_option();
2980 break;
2981
2982 case OPF_AUDIO_VOLUME_OPTION:
2983 list = get_listing_opf_adjust_audio_volume();
2984 break;
2985
2986 case OPF_EXPLOSION_OPTION:
2987 list = get_listing_opf_explosion_option();
2988 break;
2989
2990 case OPF_WEAPON_BANK_NUMBER:
2991 list = get_listing_opf_weapon_banks();
2992 break;
2993
2994 case OPF_MESSAGE_OR_STRING:
2995 list = get_listing_opf_message();
2996 break;
2997
2998 case OPF_BUILTIN_HUD_GAUGE:
2999 list = get_listing_opf_builtin_hud_gauge();
3000 break;
3001
3002 case OPF_CUSTOM_HUD_GAUGE:
3003 list = get_listing_opf_custom_hud_gauge();
3004 break;
3005
3006 case OPF_SHIP_EFFECT:
3007 list = get_listing_opf_ship_effect();
3008 break;
3009
3010 case OPF_MISSION_MOOD:
3011 list = get_listing_opf_mission_moods();
3012 break;
3013
3014 case OPF_SHIP_FLAG:
3015 list = get_listing_opf_ship_flags();
3016 break;
3017
3018 case OPF_TEAM_COLOR:
3019 list = get_listing_opf_team_colors();
3020 break;
3021
3022 case OPF_NEBULA_PATTERN:
3023 list = get_listing_opf_nebula_patterns();
3024 break;
3025
3026 case OPF_GAME_SND:
3027 list = get_listing_opf_game_snds();
3028 break;
3029
3030 case OPF_FIREBALL:
3031 list = get_listing_opf_fireball();
3032 break;
3033
3034 case OPF_SPECIES:
3035 list = get_listing_opf_species();
3036 break;
3037
3038 case OPF_LANGUAGE:
3039 list = get_listing_opf_language();
3040 break;
3041
3042 default:
3043 Int3(); // unknown OPF code
3044 list = NULL;
3045 break;
3046 }
3047
3048
3049 // skip OPF_NONE, also skip for OPF_NULL, because it takes no data (though it can take plenty of operators)
3050 if (opf == OPF_NULL || opf == OPF_NONE) {
3051 return list;
3052 }
3053
3054 // skip the special argument if we aren't at the right spot in when-argument or
3055 // every-time-argument... meaning if either w_arg or e_arg is >= 1, we can continue
3056 w_arg = find_ancestral_argument_number(OP_WHEN_ARGUMENT, parent_node);
3057 e_arg = find_ancestral_argument_number(OP_EVERY_TIME_ARGUMENT, parent_node);
3058 if (w_arg < 1 && e_arg < 1 /* && the same for any future _ARGUMENT sexps */ ) {
3059 return list;
3060 }
3061
3062 // the special item is a string and should not be added for numeric lists
3063 if (opf != OPF_NUMBER && opf != OPF_POSITIVE) {
3064 head.add_data(SEXP_ARGUMENT_STRING);
3065 }
3066
3067 if (list != NULL) {
3068 // append other list
3069 head.add_list(list);
3070 }
3071
3072 // return listing
3073 return head.next;
3074 }
3075
3076 // Goober5000
find_argument_number(int parent_node,int child_node)3077 int sexp_tree::find_argument_number(int parent_node, int child_node) {
3078 int arg_num, current_node;
3079
3080 // code moved/adapted from match_closest_operator
3081 arg_num = 0;
3082 current_node = tree_nodes[parent_node].child;
3083 while (current_node >= 0) {
3084 // found?
3085 if (current_node == child_node) {
3086 return arg_num;
3087 }
3088
3089 // continue iterating
3090 arg_num++;
3091 current_node = tree_nodes[current_node].next;
3092 }
3093
3094 // not found
3095 return -1;
3096 }
3097
3098 // Goober5000
3099 // backtrack through parents until we find the operator matching
3100 // parent_op, then find the argument we went through
find_ancestral_argument_number(int parent_op,int child_node)3101 int sexp_tree::find_ancestral_argument_number(int parent_op, int child_node) {
3102 if (child_node == -1) {
3103 return -1;
3104 }
3105
3106 int parent_node;
3107 int current_node;
3108
3109 current_node = child_node;
3110 parent_node = tree_nodes[current_node].parent;
3111
3112 while (parent_node >= 0) {
3113 // check if the parent operator is the one we're looking for
3114 if (get_operator_const(tree_nodes[parent_node].text) == parent_op) {
3115 return find_argument_number(parent_node, current_node);
3116 }
3117
3118 // continue iterating up the tree
3119 current_node = parent_node;
3120 parent_node = tree_nodes[current_node].parent;
3121 }
3122
3123 // not found
3124 return -1;
3125 }
3126
3127 /**
3128 * Gets the proper data image for the tree item's place
3129 * in its parent hierarchy.
3130 */
get_data_image(int node)3131 NodeImage sexp_tree::get_data_image(int node) {
3132 int count = get_sibling_place(node) + 1;
3133
3134 if (count <= 0) {
3135 return NodeImage::DATA;
3136 }
3137
3138 if (count % 5 != 0) {
3139 return NodeImage::DATA;
3140 }
3141
3142 int idx = (count % 100) / 5;
3143
3144 return static_cast<NodeImage>(static_cast<int>(NodeImage::DATA_00) + idx);
3145 }
3146
get_sibling_place(int node)3147 int sexp_tree::get_sibling_place(int node) {
3148 if (tree_nodes[node].parent > (int) tree_nodes.size()) {
3149 return -1;
3150 }
3151 if (tree_nodes[node].parent < 0) {
3152 return -1;
3153 }
3154
3155 sexp_tree_item* myparent = &tree_nodes[tree_nodes[node].parent];
3156
3157 if (myparent->child == -1) {
3158 return -1;
3159 }
3160
3161 sexp_tree_item* mysibling = &tree_nodes[myparent->child];
3162
3163 int count = 0;
3164 while (true) {
3165 if (mysibling == &tree_nodes[node]) {
3166 break;
3167 }
3168
3169 if (mysibling->next == -1) {
3170 break;
3171 }
3172
3173 count++;
3174 mysibling = &tree_nodes[mysibling->next];
3175 }
3176
3177 return count;
3178 }
3179
3180
get_listing_opf_null()3181 sexp_list_item* sexp_tree::get_listing_opf_null() {
3182 int i;
3183 sexp_list_item head;
3184
3185 for (i = 0; i < (int) Operators.size(); i++) {
3186 if (query_operator_return_type(i) == OPR_NULL) {
3187 head.add_op(i);
3188 }
3189 }
3190
3191 return head.next;
3192 }
3193
get_listing_opf_flexible_argument()3194 sexp_list_item* sexp_tree::get_listing_opf_flexible_argument() {
3195 int i;
3196 sexp_list_item head;
3197
3198 for (i = 0; i < (int) Operators.size(); i++) {
3199 if (query_operator_return_type(i) == OPR_FLEXIBLE_ARGUMENT) {
3200 head.add_op(i);
3201 }
3202 }
3203
3204 return head.next;
3205 }
3206
get_listing_opf_bool(int parent_node)3207 sexp_list_item* sexp_tree::get_listing_opf_bool(int parent_node) {
3208 int i, only_basic;
3209 sexp_list_item head;
3210
3211 // search for the previous goal/event operators. If found, only add the true/false
3212 // sexpressions to the list
3213 only_basic = 0;
3214 if (parent_node != -1) {
3215 int op;
3216
3217 op = get_operator_const(tree_nodes[parent_node].text);
3218 if ((op == OP_PREVIOUS_GOAL_TRUE) || (op == OP_PREVIOUS_GOAL_FALSE) || (op == OP_PREVIOUS_EVENT_TRUE)
3219 || (op == OP_PREVIOUS_EVENT_FALSE)) {
3220 only_basic = 1;
3221 }
3222
3223 }
3224
3225 for (i = 0; i < (int) Operators.size(); i++) {
3226 if (query_operator_return_type(i) == OPR_BOOL) {
3227 if (!only_basic || (only_basic && ((Operators[i].value == OP_TRUE) || (Operators[i].value == OP_FALSE)))) {
3228 head.add_op(i);
3229 }
3230 }
3231 }
3232
3233 return head.next;
3234 }
3235
get_listing_opf_positive()3236 sexp_list_item* sexp_tree::get_listing_opf_positive() {
3237 int i, z;
3238 sexp_list_item head;
3239
3240 for (i = 0; i < (int) Operators.size(); i++) {
3241 z = query_operator_return_type(i);
3242 // Goober5000's number hack
3243 if ((z == OPR_NUMBER) || (z == OPR_POSITIVE)) {
3244 head.add_op(i);
3245 }
3246 }
3247
3248 return head.next;
3249 }
3250
get_listing_opf_number()3251 sexp_list_item* sexp_tree::get_listing_opf_number() {
3252 int i, z;
3253 sexp_list_item head;
3254
3255 for (i = 0; i < (int) Operators.size(); i++) {
3256 z = query_operator_return_type(i);
3257 if ((z == OPR_NUMBER) || (z == OPR_POSITIVE)) {
3258 head.add_op(i);
3259 }
3260 }
3261
3262 return head.next;
3263 }
3264
get_listing_opf_ship(int parent_node)3265 sexp_list_item* sexp_tree::get_listing_opf_ship(int parent_node) {
3266 object* ptr;
3267 sexp_list_item head;
3268 int op = 0, dock_ship = -1, require_cap_ship = 0;
3269
3270 // look at the parent node and get the operator. Some ship lists should be filtered based
3271 // on what the parent operator is
3272 if (parent_node >= 0) {
3273 op = get_operator_const(tree_nodes[parent_node].text);
3274
3275 // get the dock_ship number of if this goal is an ai dock goal. used to prune out unwanted ships out
3276 // of the generated ship list
3277 dock_ship = -1;
3278 if (op == OP_AI_DOCK) {
3279 int z;
3280
3281 z = tree_nodes[parent_node].parent;
3282 Assert(z >= 0);
3283 Assert(!stricmp(tree_nodes[z].text, "add-ship-goal") || !stricmp(tree_nodes[z].text, "add-wing-goal")
3284 || !stricmp(tree_nodes[z].text, "add-goal"));
3285
3286 z = tree_nodes[z].child;
3287 Assert(z >= 0);
3288
3289 dock_ship = ship_name_lookup(tree_nodes[z].text, 1);
3290 Assert(dock_ship != -1);
3291 }
3292 }
3293
3294 ptr = GET_FIRST(&obj_used_list);
3295 while (ptr != END_OF_LIST(&obj_used_list)) {
3296 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
3297 if (op == OP_AI_DOCK) {
3298 // only include those ships in the list which the given ship can dock with.
3299 if ((dock_ship != ptr->instance) && ship_docking_valid(dock_ship, ptr->instance)) {
3300 head.add_data(Ships[ptr->instance].ship_name);
3301 }
3302
3303 } else if (op == OP_CAP_SUBSYS_CARGO_KNOWN_DELAY) {
3304 if (((Ship_info[Ships[ptr->instance].ship_info_index].is_huge_ship()) && // big ship
3305 !(Ships[ptr->instance].flags[Ship::Ship_Flags::Toggle_subsystem_scanning]))
3306 || // which is not flagged OR
3307 ((!(Ship_info[Ships[ptr->instance].ship_info_index].is_huge_ship())) && // small ship
3308 (Ships[ptr->instance].flags[Ship::Ship_Flags::Toggle_subsystem_scanning]))) { // which is flagged
3309
3310 head.add_data(Ships[ptr->instance].ship_name);
3311 }
3312 } else {
3313 if (!require_cap_ship || Ship_info[Ships[ptr->instance].ship_info_index].is_huge_ship()) {
3314 head.add_data(Ships[ptr->instance].ship_name);
3315 }
3316 }
3317 }
3318
3319 ptr = GET_NEXT(ptr);
3320 }
3321
3322 return head.next;
3323 }
3324
get_listing_opf_wing()3325 sexp_list_item* sexp_tree::get_listing_opf_wing() {
3326 int i;
3327 sexp_list_item head;
3328
3329 for (i = 0; i < MAX_WINGS; i++) {
3330 if (Wings[i].wave_count) {
3331 head.add_data(Wings[i].name);
3332 }
3333 }
3334
3335 return head.next;
3336 }
3337
3338 // specific types of subsystems we're looking for
3339 #define OPS_CAP_CARGO 1
3340 #define OPS_STRENGTH 2
3341 #define OPS_BEAM_TURRET 3
3342 #define OPS_AWACS 4
3343 #define OPS_ROTATE 5
3344 #define OPS_ARMOR 6
get_listing_opf_subsystem(int parent_node,int arg_index)3345 sexp_list_item* sexp_tree::get_listing_opf_subsystem(int parent_node, int arg_index) {
3346 int op, child, sh;
3347 int special_subsys = 0;
3348 sexp_list_item head;
3349 ship_subsys* subsys;
3350
3351 // determine if the parent is one of the set subsystem strength items. If so,
3352 // we want to append the "Hull" name onto the end of the menu
3353 Assert(parent_node >= 0);
3354
3355 // get the operator type of the node
3356 op = get_operator_const(tree_nodes[parent_node].text);
3357
3358 // first child node
3359 child = tree_nodes[parent_node].child;
3360 Assert(child >= 0);
3361
3362 switch (op) {
3363 // where we care about hull strength
3364 case OP_REPAIR_SUBSYSTEM:
3365 case OP_SABOTAGE_SUBSYSTEM:
3366 case OP_SET_SUBSYSTEM_STRNGTH:
3367 special_subsys = OPS_STRENGTH;
3368 break;
3369
3370 // Armor types need Hull and Shields but not Simulated Hull
3371 case OP_SET_ARMOR_TYPE:
3372 special_subsys = OPS_ARMOR;
3373 break;
3374
3375 // awacs subsystems
3376 case OP_AWACS_SET_RADIUS:
3377 special_subsys = OPS_AWACS;
3378 break;
3379
3380 // rotating
3381 case OP_LOCK_ROTATING_SUBSYSTEM:
3382 case OP_FREE_ROTATING_SUBSYSTEM:
3383 case OP_REVERSE_ROTATING_SUBSYSTEM:
3384 case OP_ROTATING_SUBSYS_SET_TURN_TIME:
3385 special_subsys = OPS_ROTATE;
3386 break;
3387
3388 // where we care about capital ship subsystem cargo
3389 case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
3390 special_subsys = OPS_CAP_CARGO;
3391
3392 // get the next sibling
3393 child = tree_nodes[child].next;
3394 break;
3395
3396 // where we care about turrets carrying beam weapons
3397 case OP_BEAM_FIRE:
3398 special_subsys = OPS_BEAM_TURRET;
3399
3400 // if this is arg index 3 (targeted ship)
3401 if (arg_index == 3) {
3402 special_subsys = OPS_STRENGTH;
3403
3404 // iterate to the next field two times
3405 child = tree_nodes[child].next;
3406 Assert(child >= 0);
3407 child = tree_nodes[child].next;
3408 } else {
3409 Assert(arg_index == 1);
3410 }
3411 break;
3412
3413 case OP_BEAM_FIRE_COORDS:
3414 special_subsys = OPS_BEAM_TURRET;
3415 break;
3416
3417 // these sexps check the subsystem of the *second entry* on the list, not the first
3418 case OP_DISTANCE_CENTER_SUBSYSTEM:
3419 case OP_DISTANCE_BBOX_SUBSYSTEM:
3420 case OP_SET_CARGO:
3421 case OP_IS_CARGO:
3422 case OP_CHANGE_AI_CLASS:
3423 case OP_IS_AI_CLASS:
3424 case OP_MISSILE_LOCKED:
3425 case OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD:
3426 case OP_IS_IN_TURRET_FOV:
3427 // iterate to the next field
3428 child = tree_nodes[child].next;
3429 break;
3430
3431 // this sexp checks the subsystem of the *fourth entry* on the list
3432 case OP_QUERY_ORDERS:
3433 // iterate to the next field three times
3434 child = tree_nodes[child].next;
3435 Assert(child >= 0);
3436 child = tree_nodes[child].next;
3437 Assert(child >= 0);
3438 child = tree_nodes[child].next;
3439 break;
3440
3441 // this sexp checks the subsystem of the *seventh entry* on the list
3442 case OP_BEAM_FLOATING_FIRE:
3443 // iterate to the next field six times
3444 child = tree_nodes[child].next;
3445 Assert(child >= 0);
3446 child = tree_nodes[child].next;
3447 Assert(child >= 0);
3448 child = tree_nodes[child].next;
3449 Assert(child >= 0);
3450 child = tree_nodes[child].next;
3451 Assert(child >= 0);
3452 child = tree_nodes[child].next;
3453 Assert(child >= 0);
3454 child = tree_nodes[child].next;
3455 break;
3456
3457 // this sexp checks the subsystem of the *ninth entry* on the list
3458 case OP_WEAPON_CREATE:
3459 // iterate to the next field eight times
3460 child = tree_nodes[child].next;
3461 Assert(child >= 0);
3462 child = tree_nodes[child].next;
3463 Assert(child >= 0);
3464 child = tree_nodes[child].next;
3465 Assert(child >= 0);
3466 child = tree_nodes[child].next;
3467 Assert(child >= 0);
3468 child = tree_nodes[child].next;
3469 Assert(child >= 0);
3470 child = tree_nodes[child].next;
3471 Assert(child >= 0);
3472 child = tree_nodes[child].next;
3473 Assert(child >= 0);
3474 child = tree_nodes[child].next;
3475 break;
3476 }
3477
3478 // now find the ship and add all relevant subsystems
3479 Assert(child >= 0);
3480 sh = ship_name_lookup(tree_nodes[child].text, 1);
3481 if (sh >= 0) {
3482 subsys = GET_FIRST(&Ships[sh].subsys_list);
3483 while (subsys != END_OF_LIST(&Ships[sh].subsys_list)) {
3484 // add stuff
3485 switch (special_subsys) {
3486 // subsystem cargo
3487 case OPS_CAP_CARGO:
3488 head.add_data(subsys->system_info->subobj_name);
3489 break;
3490
3491 // beam fire
3492 case OPS_BEAM_TURRET:
3493 head.add_data(subsys->system_info->subobj_name);
3494 break;
3495
3496 // awacs level
3497 case OPS_AWACS:
3498 if (subsys->system_info->flags[Model::Subsystem_Flags::Awacs]) {
3499 head.add_data(subsys->system_info->subobj_name);
3500 }
3501 break;
3502
3503 // rotating
3504 case OPS_ROTATE:
3505 if (subsys->system_info->flags[Model::Subsystem_Flags::Rotates]) {
3506 head.add_data(subsys->system_info->subobj_name);
3507 }
3508 break;
3509
3510 // everything else
3511 default:
3512 head.add_data(subsys->system_info->subobj_name);
3513 break;
3514 }
3515
3516 // next subsystem
3517 subsys = GET_NEXT(subsys);
3518 }
3519 }
3520
3521 // if one of the subsystem strength operators, append the Hull string and the Simulated Hull string
3522 if (special_subsys == OPS_STRENGTH) {
3523 head.add_data(SEXP_HULL_STRING);
3524 head.add_data(SEXP_SIM_HULL_STRING);
3525 }
3526 // if setting armor type we only need Hull and Shields
3527 if (special_subsys == OPS_ARMOR) {
3528 head.add_data(SEXP_HULL_STRING);
3529 head.add_data(SEXP_SHIELD_STRING);
3530 }
3531
3532 return head.next;
3533 }
3534
get_listing_opf_subsystem_type(int parent_node)3535 sexp_list_item* sexp_tree::get_listing_opf_subsystem_type(int parent_node) {
3536 int i, child, shipnum, num_added = 0;
3537 sexp_list_item head;
3538 ship_subsys* subsys;
3539
3540 // first child node
3541 child = tree_nodes[parent_node].child;
3542 Assert(child >= 0);
3543
3544 // now find the ship
3545 shipnum = ship_name_lookup(tree_nodes[child].text, 1);
3546 if (shipnum < 0) {
3547 return head.next;
3548 }
3549
3550 // add all relevant subsystem types
3551 for (i = 0; i < SUBSYSTEM_MAX; i++) {
3552 // don't allow these two
3553 if (i == SUBSYSTEM_NONE || i == SUBSYSTEM_UNKNOWN) {
3554 continue;
3555 }
3556
3557 // loop through all ship subsystems
3558 subsys = GET_FIRST(&Ships[shipnum].subsys_list);
3559 while (subsys != END_OF_LIST(&Ships[shipnum].subsys_list)) {
3560 // check if this subsystem is of this type
3561 if (i == subsys->system_info->type) {
3562 // subsystem type is applicable, so add it
3563 head.add_data(Subsystem_types[i]);
3564 num_added++;
3565 break;
3566 }
3567
3568 // next subsystem
3569 subsys = GET_NEXT(subsys);
3570 }
3571 }
3572
3573 // if no subsystem types, go ahead and add NONE (even though it won't be checked)
3574 if (num_added == 0) {
3575 head.add_data(Subsystem_types[SUBSYSTEM_NONE]);
3576 }
3577
3578 return head.next;
3579 }
3580
get_listing_opf_point()3581 sexp_list_item* sexp_tree::get_listing_opf_point() {
3582 char buf[NAME_LENGTH + 8];
3583 SCP_list<waypoint_list>::iterator ii;
3584 int j;
3585 sexp_list_item head;
3586
3587 for (ii = Waypoint_lists.begin(); ii != Waypoint_lists.end(); ++ii) {
3588 for (j = 0; (uint) j < ii->get_waypoints().size(); ++j) {
3589 sprintf(buf, "%s:%d", ii->get_name(), j + 1);
3590 head.add_data_dup(buf);
3591 }
3592 }
3593
3594 return head.next;
3595 }
3596
get_listing_opf_iff()3597 sexp_list_item* sexp_tree::get_listing_opf_iff() {
3598 int i;
3599 sexp_list_item head;
3600
3601 for (i = 0; i < Num_iffs; i++) {
3602 head.add_data(Iff_info[i].iff_name);
3603 }
3604
3605 return head.next;
3606 }
3607
get_listing_opf_ai_class()3608 sexp_list_item* sexp_tree::get_listing_opf_ai_class() {
3609 int i;
3610 sexp_list_item head;
3611
3612 for (i = 0; i < Num_ai_classes; i++) {
3613 head.add_data(Ai_class_names[i]);
3614 }
3615
3616 return head.next;
3617 }
3618
get_listing_opf_support_ship_class()3619 sexp_list_item* sexp_tree::get_listing_opf_support_ship_class() {
3620 sexp_list_item head;
3621
3622 head.add_data("<species support ship class>");
3623
3624 for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
3625 if (it->flags[Ship::Info_Flags::Support]) {
3626 head.add_data(it->name);
3627 }
3628 }
3629
3630 return head.next;
3631 }
3632
get_listing_opf_ssm_class()3633 sexp_list_item* sexp_tree::get_listing_opf_ssm_class() {
3634 sexp_list_item head;
3635
3636 for (auto it = Ssm_info.cbegin(); it != Ssm_info.cend(); ++it) {
3637 head.add_data(it->name);
3638 }
3639
3640 return head.next;
3641 }
3642
get_listing_opf_ship_with_bay()3643 sexp_list_item* sexp_tree::get_listing_opf_ship_with_bay() {
3644 object* objp;
3645 sexp_list_item head;
3646
3647 head.add_data("<no anchor>");
3648
3649 for (objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) {
3650 if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
3651 // determine if this ship has a docking bay
3652 if (ship_has_dock_bay(objp->instance)) {
3653 head.add_data(Ships[objp->instance].ship_name);
3654 }
3655 }
3656 }
3657
3658 return head.next;
3659 }
3660
get_listing_opf_soundtrack_name()3661 sexp_list_item* sexp_tree::get_listing_opf_soundtrack_name() {
3662 int i;
3663 sexp_list_item head;
3664
3665 head.add_data("<No Music>");
3666
3667 for (i = 0; i < Num_soundtracks; i++) {
3668 head.add_data(Soundtracks[i].name);
3669 }
3670
3671 return head.next;
3672 }
3673
get_listing_opf_arrival_location()3674 sexp_list_item* sexp_tree::get_listing_opf_arrival_location() {
3675 int i;
3676 sexp_list_item head;
3677
3678 for (i = 0; i < MAX_ARRIVAL_NAMES; i++) {
3679 head.add_data(Arrival_location_names[i]);
3680 }
3681
3682 return head.next;
3683 }
3684
get_listing_opf_departure_location()3685 sexp_list_item* sexp_tree::get_listing_opf_departure_location() {
3686 int i;
3687 sexp_list_item head;
3688
3689 for (i = 0; i < MAX_DEPARTURE_NAMES; i++) {
3690 head.add_data(Departure_location_names[i]);
3691 }
3692
3693 return head.next;
3694 }
3695
get_listing_opf_arrival_anchor_all()3696 sexp_list_item* sexp_tree::get_listing_opf_arrival_anchor_all() {
3697 int i, restrict_to_players;
3698 object* objp;
3699 sexp_list_item head;
3700
3701 for (restrict_to_players = 0; restrict_to_players < 2; restrict_to_players++) {
3702 for (i = 0; i < Num_iffs; i++) {
3703 char tmp[NAME_LENGTH + 15];
3704 stuff_special_arrival_anchor_name(tmp, i, restrict_to_players, 0);
3705
3706 head.add_data_dup(tmp);
3707 }
3708 }
3709
3710 for (objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) {
3711 if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
3712 head.add_data(Ships[objp->instance].ship_name);
3713 }
3714 }
3715
3716 return head.next;
3717 }
3718
get_listing_opf_ai_goal(int parent_node)3719 sexp_list_item* sexp_tree::get_listing_opf_ai_goal(int parent_node) {
3720 int i, n, w, z, child;
3721 sexp_list_item head;
3722
3723 Assert(parent_node >= 0);
3724 child = tree_nodes[parent_node].child;
3725 Assert(child >= 0);
3726 n = ship_name_lookup(tree_nodes[child].text, 1);
3727 if (n >= 0) {
3728 // add operators if it's an ai-goal and ai-goal is allowed for that ship
3729 for (i = 0; i < (int) Operators.size(); i++) {
3730 if ((query_operator_return_type(i) == OPR_AI_GOAL) && query_sexp_ai_goal_valid(Operators[i].value, n)) {
3731 head.add_op(i);
3732 }
3733 }
3734
3735 } else {
3736 z = wing_name_lookup(tree_nodes[child].text);
3737 if (z >= 0) {
3738 for (w = 0; w < Wings[z].wave_count; w++) {
3739 n = Wings[z].ship_index[w];
3740 // add operators if it's an ai-goal and ai-goal is allowed for that ship
3741 for (i = 0; i < (int) Operators.size(); i++) {
3742 if ((query_operator_return_type(i) == OPR_AI_GOAL)
3743 && query_sexp_ai_goal_valid(Operators[i].value, n)) {
3744 head.add_op(i);
3745 }
3746 }
3747 }
3748 // when dealing with the special argument add them all. It's up to the FREDder to ensure invalid orders aren't given
3749 } else if (!strcmp(tree_nodes[child].text, SEXP_ARGUMENT_STRING)) {
3750 for (i = 0; i < (int) Operators.size(); i++) {
3751 if (query_operator_return_type(i) == OPR_AI_GOAL) {
3752 head.add_op(i);
3753 }
3754 }
3755 } else {
3756 return NULL;
3757 } // no valid ship or wing to check against, make nothing available
3758 }
3759
3760 return head.next;
3761 }
3762
get_listing_opf_docker_point(int parent_node)3763 sexp_list_item* sexp_tree::get_listing_opf_docker_point(int parent_node) {
3764 int z;
3765 sexp_list_item head;
3766 int sh = -1;
3767
3768 Assert(parent_node >= 0);
3769 Assert(!stricmp(tree_nodes[parent_node].text, "ai-dock") || !stricmp(tree_nodes[parent_node].text, "set-docked"));
3770
3771 if (!stricmp(tree_nodes[parent_node].text, "ai-dock")) {
3772 z = tree_nodes[parent_node].parent;
3773 Assert(z >= 0);
3774 Assert(!stricmp(tree_nodes[z].text, "add-ship-goal") || !stricmp(tree_nodes[z].text, "add-wing-goal")
3775 || !stricmp(tree_nodes[z].text, "add-goal"));
3776
3777 z = tree_nodes[z].child;
3778 Assert(z >= 0);
3779
3780 sh = ship_name_lookup(tree_nodes[z].text, 1);
3781 } else if (!stricmp(tree_nodes[parent_node].text, "set-docked")) {
3782 //Docker ship should be the first child node
3783 z = tree_nodes[parent_node].child;
3784 sh = ship_name_lookup(tree_nodes[z].text, 1);
3785 }
3786
3787 if (sh >= 0) {
3788 auto list = _editor->get_docking_list(Ship_info[Ships[sh].ship_info_index].model_num);
3789 for (auto& dock : list) {
3790 head.add_data(dock.c_str());
3791 }
3792 }
3793
3794 return head.next;
3795 }
3796
get_listing_opf_dockee_point(int parent_node)3797 sexp_list_item* sexp_tree::get_listing_opf_dockee_point(int parent_node) {
3798 int z;
3799 sexp_list_item head;
3800 int sh = -1;
3801
3802 Assert(parent_node >= 0);
3803 Assert(!stricmp(tree_nodes[parent_node].text, "ai-dock") || !stricmp(tree_nodes[parent_node].text, "set-docked"));
3804
3805 if (!stricmp(tree_nodes[parent_node].text, "ai-dock")) {
3806 z = tree_nodes[parent_node].child;
3807 Assert(z >= 0);
3808
3809 sh = ship_name_lookup(tree_nodes[z].text, 1);
3810 } else if (!stricmp(tree_nodes[parent_node].text, "set-docked")) {
3811 //Dockee ship should be the third child node
3812 z = tree_nodes[parent_node].child; // 1
3813 z = tree_nodes[z].next; // 2
3814 z = tree_nodes[z].next; // 3
3815
3816 sh = ship_name_lookup(tree_nodes[z].text, 1);
3817 }
3818
3819 if (sh >= 0) {
3820 auto list = _editor->get_docking_list(Ship_info[Ships[sh].ship_info_index].model_num);
3821 for (auto& dock : list) {
3822 head.add_data(dock.c_str());
3823 }
3824 }
3825
3826 return head.next;
3827 }
3828
get_listing_opf_message()3829 sexp_list_item* sexp_tree::get_listing_opf_message() {
3830 sexp_list_item head;
3831
3832 for (auto& msg : _interface->getMessages()) {
3833 head.add_data(msg.c_str());
3834 }
3835
3836 return head.next;
3837 }
3838
get_listing_opf_persona()3839 sexp_list_item* sexp_tree::get_listing_opf_persona() {
3840 int i;
3841 sexp_list_item head;
3842
3843 for (i = 0; i < Num_personas; i++) {
3844 if (Personas[i].flags & PERSONA_FLAG_WINGMAN) {
3845 head.add_data(Personas[i].name);
3846 }
3847 }
3848
3849 return head.next;
3850 }
3851
get_listing_opf_font()3852 sexp_list_item* sexp_tree::get_listing_opf_font() {
3853 int i;
3854 sexp_list_item head;
3855
3856 for (i = 0; i < font::FontManager::numberOfFonts(); i++) {
3857 head.add_data(const_cast<char*>(font::FontManager::getFont(i)->getName().c_str()));
3858 }
3859
3860 return head.next;
3861 }
3862
get_listing_opf_who_from()3863 sexp_list_item* sexp_tree::get_listing_opf_who_from() {
3864 object* ptr;
3865 sexp_list_item head;
3866
3867 //head.add_data("<any allied>");
3868 head.add_data("#Command");
3869 head.add_data("<any wingman>");
3870 head.add_data("<none>");
3871
3872 ptr = GET_FIRST(&obj_used_list);
3873 while (ptr != END_OF_LIST(&obj_used_list)) {
3874 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
3875 if (Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].is_flyable()) {
3876 head.add_data(Ships[ptr->instance].ship_name);
3877 }
3878 }
3879
3880 ptr = GET_NEXT(ptr);
3881 }
3882
3883 return head.next;
3884 }
3885
get_listing_opf_priority()3886 sexp_list_item* sexp_tree::get_listing_opf_priority() {
3887 sexp_list_item head;
3888
3889 head.add_data("High");
3890 head.add_data("Normal");
3891 head.add_data("Low");
3892 return head.next;
3893 }
3894
get_listing_opf_sound_environment()3895 sexp_list_item* sexp_tree::get_listing_opf_sound_environment() {
3896 sexp_list_item head;
3897
3898 head.add_data(SEXP_NONE_STRING);
3899 for (int i = 0; i < (int) EFX_presets.size(); i++) {
3900 head.add_data_dup(EFX_presets[i].name.c_str());
3901 }
3902
3903 return head.next;
3904 }
3905
get_listing_opf_sound_environment_option()3906 sexp_list_item* sexp_tree::get_listing_opf_sound_environment_option() {
3907 sexp_list_item head;
3908
3909 for (int i = 0; i < Num_sound_environment_options; i++) {
3910 head.add_data(Sound_environment_option[i]);
3911 }
3912
3913 return head.next;
3914 }
3915
get_listing_opf_adjust_audio_volume()3916 sexp_list_item* sexp_tree::get_listing_opf_adjust_audio_volume() {
3917 sexp_list_item head;
3918
3919 for (int i = 0; i < Num_adjust_audio_options; i++) {
3920 head.add_data(Adjust_audio_options[i]);
3921 }
3922
3923 return head.next;
3924 }
3925
get_listing_opf_builtin_hud_gauge()3926 sexp_list_item* sexp_tree::get_listing_opf_builtin_hud_gauge() {
3927 sexp_list_item head;
3928
3929 for (int i = 0; i < Num_hud_gauge_types; i++) {
3930 head.add_data(Hud_gauge_types[i].name);
3931 }
3932
3933 return head.next;
3934 }
3935
get_listing_opf_custom_hud_gauge()3936 sexp_list_item *sexp_tree::get_listing_opf_custom_hud_gauge()
3937 {
3938 sexp_list_item head;
3939 SCP_unordered_set<SCP_string> all_gauges;
3940
3941 for (auto &gauge : default_hud_gauges)
3942 {
3943 all_gauges.insert(gauge->getCustomGaugeName());
3944 head.add_data(gauge->getCustomGaugeName());
3945 }
3946
3947 for (auto &si : Ship_info)
3948 {
3949 for (auto &gauge : si.hud_gauges)
3950 {
3951 // avoid duplicating any HUD gauges
3952 if (all_gauges.count(gauge->getCustomGaugeName()) == 0)
3953 {
3954 all_gauges.insert(gauge->getCustomGaugeName());
3955 head.add_data(gauge->getCustomGaugeName());
3956 }
3957 }
3958 }
3959
3960 return head.next;
3961 }
3962
get_listing_opf_ship_effect()3963 sexp_list_item* sexp_tree::get_listing_opf_ship_effect() {
3964 sexp_list_item head;
3965
3966 for (SCP_vector<ship_effect>::iterator sei = Ship_effects.begin(); sei != Ship_effects.end(); ++sei) {
3967 head.add_data_dup(sei->name);
3968 }
3969
3970 return head.next;
3971 }
3972
get_listing_opf_explosion_option()3973 sexp_list_item* sexp_tree::get_listing_opf_explosion_option() {
3974 sexp_list_item head;
3975
3976 for (int i = 0; i < Num_explosion_options; i++) {
3977 head.add_data(Explosion_option[i]);
3978 }
3979
3980 return head.next;
3981 }
3982
get_listing_opf_waypoint_path()3983 sexp_list_item* sexp_tree::get_listing_opf_waypoint_path() {
3984 SCP_list<waypoint_list>::iterator ii;
3985 sexp_list_item head;
3986
3987 for (ii = Waypoint_lists.begin(); ii != Waypoint_lists.end(); ++ii) {
3988 head.add_data(ii->get_name());
3989 }
3990
3991 return head.next;
3992 }
3993
get_listing_opf_ship_point()3994 sexp_list_item* sexp_tree::get_listing_opf_ship_point() {
3995 sexp_list_item head;
3996
3997 head.add_list(get_listing_opf_ship());
3998 head.add_list(get_listing_opf_point());
3999
4000 return head.next;
4001 }
4002
get_listing_opf_ship_wing_wholeteam()4003 sexp_list_item* sexp_tree::get_listing_opf_ship_wing_wholeteam() {
4004 int i;
4005 sexp_list_item head;
4006
4007 for (i = 0; i < Num_iffs; i++) {
4008 head.add_data(Iff_info[i].iff_name);
4009 }
4010
4011 head.add_list(get_listing_opf_ship_wing());
4012
4013 return head.next;
4014 }
4015
get_listing_opf_ship_wing_shiponteam_point()4016 sexp_list_item* sexp_tree::get_listing_opf_ship_wing_shiponteam_point() {
4017 int i;
4018 sexp_list_item head;
4019
4020 for (i = 0; i < Num_iffs; i++) {
4021 SCP_string tmp;
4022 sprintf(tmp, "<any %s>", Iff_info[i].iff_name);
4023 std::transform(begin(tmp), end(tmp), begin(tmp), [](char c) { return (char)::tolower(c); });
4024 head.add_data_dup(tmp.c_str());
4025 }
4026
4027 head.add_list(get_listing_opf_ship_wing_point());
4028
4029 return head.next;
4030 }
4031
get_listing_opf_ship_wing_point()4032 sexp_list_item* sexp_tree::get_listing_opf_ship_wing_point() {
4033 sexp_list_item head;
4034
4035 head.add_list(get_listing_opf_ship());
4036 head.add_list(get_listing_opf_wing());
4037 head.add_list(get_listing_opf_point());
4038
4039 return head.next;
4040 }
4041
get_listing_opf_ship_wing_point_or_none()4042 sexp_list_item* sexp_tree::get_listing_opf_ship_wing_point_or_none() {
4043 sexp_list_item head;
4044
4045 head.add_data(SEXP_NONE_STRING);
4046 head.add_list(get_listing_opf_ship_wing_point());
4047
4048 return head.next;
4049 }
4050
get_listing_opf_mission_name()4051 sexp_list_item* sexp_tree::get_listing_opf_mission_name() {
4052 sexp_list_item head;
4053
4054 for (auto& mission : _interface->getMissionNames()) {
4055 head.add_data(mission.c_str());
4056 }
4057
4058 // This code is kept until the campaign editor is added
4059 /*
4060 if ((m_mode == MODE_CAMPAIGN) && (Cur_campaign_mission >= 0)) {
4061 for (i = 0; i < Campaign.num_missions; i++)
4062 if ((i == Cur_campaign_mission)
4063 || (Campaign.missions[i].level < Campaign.missions[Cur_campaign_mission].level))
4064 head.add_data(Campaign.missions[i].name);
4065
4066 } else
4067 head.add_data(Mission_filename);
4068 */
4069
4070 return head.next;
4071 }
4072
get_listing_opf_goal_name(int parent_node)4073 sexp_list_item* sexp_tree::get_listing_opf_goal_name(int parent_node) {
4074 sexp_list_item head;
4075
4076 Assert(parent_node >= 0);
4077 auto child = tree_nodes[parent_node].child;
4078
4079 // This is used by the campaign editor to show the entries for specific missions
4080 SCP_string reference;
4081 if (child >= 0) {
4082 reference = tree_nodes[child].text;
4083 }
4084
4085 for (auto& entry : _interface->getMissionGoals(reference)) {
4086 head.add_data(entry.c_str());
4087 }
4088
4089 // This code is kept for reference purposes until the campaign editor is added
4090 /*
4091 if (m_mode == MODE_CAMPAIGN) {
4092 int child;
4093
4094 Assert(parent_node >= 0);
4095 child = tree_nodes[parent_node].child;
4096 Assert(child >= 0);
4097
4098 for (m = 0; m < Campaign.num_missions; m++)
4099 if (!stricmp(Campaign.missions[m].name, tree_nodes[child].text))
4100 break;
4101
4102 if (m < Campaign.num_missions) {
4103 if (Campaign.missions[m].num_goals < 0) // haven't loaded goal names yet.
4104 read_mission_goal_list(m);
4105
4106 for (i = 0; i < Campaign.missions[m].num_goals; i++)
4107 head.add_data(Campaign.missions[m].goals[i].name);
4108 }
4109
4110 } else {
4111 for (i = 0; i < Num_goals; i++)
4112 head.add_data(Mission_goals[i].name);
4113 }
4114 */
4115
4116 return head.next;
4117 }
4118
get_listing_opf_ship_wing()4119 sexp_list_item* sexp_tree::get_listing_opf_ship_wing() {
4120 sexp_list_item head;
4121
4122 head.add_list(get_listing_opf_ship());
4123 head.add_list(get_listing_opf_wing());
4124
4125 return head.next;
4126 }
4127
get_listing_opf_order_recipient()4128 sexp_list_item* sexp_tree::get_listing_opf_order_recipient() {
4129 sexp_list_item head;
4130
4131 head.add_data("<all fighters>");
4132
4133 head.add_list(get_listing_opf_ship());
4134 head.add_list(get_listing_opf_wing());
4135 return head.next;
4136 }
4137
get_listing_opf_ship_type()4138 sexp_list_item* sexp_tree::get_listing_opf_ship_type() {
4139 unsigned int i;
4140 sexp_list_item head;
4141
4142 for (i = 0; i < Ship_types.size(); i++) {
4143 head.add_data(Ship_types[i].name);
4144 }
4145
4146 return head.next;
4147 }
4148
get_listing_opf_keypress()4149 sexp_list_item* sexp_tree::get_listing_opf_keypress() {
4150 sexp_list_item head;
4151 const auto& Default_config = Control_config_presets[0].bindings;
4152
4153 for (size_t i = 0; i < Control_config.size(); ++i) {
4154 auto btn = Default_config[i].get_btn(CID_KEYBOARD);
4155
4156 if ((btn >= -1) && !Control_config[i].disabled) {
4157 head.add_data_dup(textify_scancode(btn));
4158 }
4159 }
4160
4161 return head.next;
4162 }
4163
get_listing_opf_event_name(int parent_node)4164 sexp_list_item* sexp_tree::get_listing_opf_event_name(int parent_node) {
4165 sexp_list_item head;
4166
4167 Assert(parent_node >= 0);
4168 auto child = tree_nodes[parent_node].child;
4169
4170 // This is used by the campaign editor to show the entries for specific missions
4171 SCP_string reference;
4172 if (child >= 0) {
4173 reference = tree_nodes[child].text;
4174 }
4175
4176 for (auto& entry : _interface->getMissionEvents(reference)) {
4177 head.add_data(entry.c_str());
4178 }
4179
4180 // This code is kept until the campaign editor is added
4181 /*
4182 if (m_mode == MODE_CAMPAIGN) {
4183 Assert(parent_node >= 0);
4184 auto child = tree_nodes[parent_node].child;
4185 Assert(child >= 0);
4186
4187 for (m = 0; m < Campaign.num_missions; m++)
4188 if (!stricmp(Campaign.missions[m].name, tree_nodes[child].text))
4189 break;
4190
4191 if (m < Campaign.num_missions) {
4192 if (Campaign.missions[m].num_events < 0) // haven't loaded goal names yet.
4193 read_mission_goal_list(m);
4194
4195 for (i = 0; i < Campaign.missions[m].num_events; i++)
4196 head.add_data(Campaign.missions[m].events[i].name);
4197 }
4198 } else {
4199 for (i = 0; i < Num_mission_events; i++)
4200 head.add_data(Mission_events[i].name);
4201 }
4202 */
4203
4204 return head.next;
4205 }
4206
get_listing_opf_ai_order()4207 sexp_list_item* sexp_tree::get_listing_opf_ai_order() {
4208 int i;
4209 sexp_list_item head;
4210
4211 for (i = 0; i < NUM_COMM_ORDER_ITEMS; i++) {
4212 head.add_data(Comm_orders[i].name.c_str());
4213 }
4214
4215 return head.next;
4216 }
4217
get_listing_opf_skill_level()4218 sexp_list_item* sexp_tree::get_listing_opf_skill_level() {
4219 int i;
4220 sexp_list_item head;
4221
4222 for (i = 0; i < NUM_SKILL_LEVELS; i++) {
4223 head.add_data(Skill_level_names(i, 0));
4224 }
4225
4226 return head.next;
4227 }
4228
get_listing_opf_cargo()4229 sexp_list_item* sexp_tree::get_listing_opf_cargo() {
4230 sexp_list_item head;
4231
4232 head.add_data("Nothing");
4233 for (int i = 0; i < Num_cargo; i++) {
4234 if (stricmp(Cargo_names[i], "nothing") != 0) {
4235 head.add_data(Cargo_names[i]);
4236 }
4237 }
4238
4239 return head.next;
4240 }
4241
get_listing_opf_string()4242 sexp_list_item* sexp_tree::get_listing_opf_string() {
4243 sexp_list_item head;
4244
4245 head.add_data(SEXP_ANY_STRING);
4246
4247 return head.next;
4248 }
4249
get_listing_opf_medal_name()4250 sexp_list_item* sexp_tree::get_listing_opf_medal_name() {
4251 int i;
4252 sexp_list_item head;
4253
4254 for (i = 0; i < Num_medals; i++) {
4255 // don't add Rank or the Ace badges
4256 if ((i == Rank_medal_index) || (Medals[i].kills_needed > 0)) {
4257 continue;
4258 }
4259 head.add_data(Medals[i].name);
4260 }
4261
4262 return head.next;
4263 }
4264
get_listing_opf_weapon_name()4265 sexp_list_item* sexp_tree::get_listing_opf_weapon_name() {
4266 sexp_list_item head;
4267
4268 for (auto &wi : Weapon_info) {
4269 head.add_data(wi.name);
4270 }
4271
4272 return head.next;
4273 }
4274
get_listing_opf_intel_name()4275 sexp_list_item* sexp_tree::get_listing_opf_intel_name() {
4276 sexp_list_item head;
4277
4278 for (auto &ii : Intel_info) {
4279 head.add_data(ii.name);
4280 }
4281
4282 return head.next;
4283 }
4284
get_listing_opf_ship_class_name()4285 sexp_list_item* sexp_tree::get_listing_opf_ship_class_name() {
4286 sexp_list_item head;
4287
4288 for (auto &si : Ship_info) {
4289 head.add_data(si.name);
4290 }
4291
4292 return head.next;
4293 }
4294
get_listing_opf_huge_weapon()4295 sexp_list_item* sexp_tree::get_listing_opf_huge_weapon() {
4296 sexp_list_item head;
4297
4298 for (auto &wi : Weapon_info) {
4299 if (wi.wi_flags[Weapon::Info_Flags::Huge]) {
4300 head.add_data(wi.name);
4301 }
4302 }
4303
4304 return head.next;
4305 }
4306
get_listing_opf_ship_not_player()4307 sexp_list_item* sexp_tree::get_listing_opf_ship_not_player() {
4308 object* ptr;
4309 sexp_list_item head;
4310
4311 ptr = GET_FIRST(&obj_used_list);
4312 while (ptr != END_OF_LIST(&obj_used_list)) {
4313 if (ptr->type == OBJ_SHIP) {
4314 head.add_data(Ships[ptr->instance].ship_name);
4315 }
4316
4317 ptr = GET_NEXT(ptr);
4318 }
4319
4320 return head.next;
4321 }
4322
get_listing_opf_ship_or_none()4323 sexp_list_item* sexp_tree::get_listing_opf_ship_or_none() {
4324 sexp_list_item head;
4325
4326 head.add_data(SEXP_NONE_STRING);
4327 head.add_list(get_listing_opf_ship());
4328
4329 return head.next;
4330 }
4331
get_listing_opf_subsystem_or_none(int parent_node,int arg_index)4332 sexp_list_item* sexp_tree::get_listing_opf_subsystem_or_none(int parent_node, int arg_index) {
4333 sexp_list_item head;
4334
4335 head.add_data(SEXP_NONE_STRING);
4336 head.add_list(get_listing_opf_subsystem(parent_node, arg_index));
4337
4338 return head.next;
4339 }
4340
get_listing_opf_subsys_or_generic(int parent_node,int arg_index)4341 sexp_list_item* sexp_tree::get_listing_opf_subsys_or_generic(int parent_node, int arg_index) {
4342 sexp_list_item head;
4343
4344 head.add_data(SEXP_ALL_ENGINES_STRING);
4345 head.add_data(SEXP_ALL_TURRETS_STRING);
4346 head.add_list(get_listing_opf_subsystem(parent_node, arg_index));
4347
4348 return head.next;
4349 }
4350
get_listing_opf_jump_nodes()4351 sexp_list_item* sexp_tree::get_listing_opf_jump_nodes() {
4352 sexp_list_item head;
4353
4354 SCP_list<CJumpNode>::iterator jnp;
4355 for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
4356 head.add_data(jnp->GetName());
4357 }
4358
4359 return head.next;
4360 }
4361
4362 // creates list of Sexp_variables
get_listing_opf_variable_names()4363 sexp_list_item* sexp_tree::get_listing_opf_variable_names() {
4364 int i;
4365 sexp_list_item head;
4366
4367 for (i = 0; i < MAX_SEXP_VARIABLES; i++) {
4368 if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
4369 head.add_data(Sexp_variables[i].variable_name);
4370 }
4371 }
4372
4373 return head.next;
4374 }
4375
4376 // get default skybox model name
get_listing_opf_skybox_model()4377 sexp_list_item* sexp_tree::get_listing_opf_skybox_model() {
4378
4379 sexp_list_item head;
4380 head.add_data("default");
4381 return head.next;
4382 }
4383
get_listing_opf_skybox_flags()4384 sexp_list_item* sexp_tree::get_listing_opf_skybox_flags() {
4385 sexp_list_item head;
4386 int i;
4387
4388 for (i = 0; i < Num_skybox_flags; ++i) {
4389 head.add_data(Skybox_flags[i]);
4390 }
4391 return head.next;
4392 }
4393
get_listing_opf_background_bitmap()4394 sexp_list_item* sexp_tree::get_listing_opf_background_bitmap() {
4395 sexp_list_item head;
4396 int i;
4397
4398 for (i = 0; i < stars_get_num_entries(false, true); i++) {
4399 head.add_data(stars_get_name_FRED(i, false));
4400 }
4401
4402 return head.next;
4403 }
4404
get_listing_opf_sun_bitmap()4405 sexp_list_item* sexp_tree::get_listing_opf_sun_bitmap() {
4406 sexp_list_item head;
4407 int i;
4408
4409 for (i = 0; i < stars_get_num_entries(true, true); i++) {
4410 head.add_data(stars_get_name_FRED(i, true));
4411 }
4412
4413 return head.next;
4414 }
4415
get_listing_opf_nebula_storm_type()4416 sexp_list_item* sexp_tree::get_listing_opf_nebula_storm_type() {
4417 sexp_list_item head;
4418
4419 head.add_data(SEXP_NONE_STRING);
4420
4421 for (size_t i = 0; i < Storm_types.size(); i++) {
4422 head.add_data(Storm_types[i].name);
4423 }
4424
4425 return head.next;
4426 }
4427
get_listing_opf_nebula_poof()4428 sexp_list_item* sexp_tree::get_listing_opf_nebula_poof() {
4429 sexp_list_item head;
4430
4431 for (poof_info &pf : Poof_info) {
4432 head.add_data(pf.name);
4433 }
4434
4435 return head.next;
4436 }
4437
get_listing_opf_turret_target_order()4438 sexp_list_item* sexp_tree::get_listing_opf_turret_target_order() {
4439 int i;
4440 sexp_list_item head;
4441
4442 for (i = 0; i < NUM_TURRET_ORDER_TYPES; i++) {
4443 head.add_data(Turret_target_order_names[i]);
4444 }
4445
4446 return head.next;
4447 }
4448
get_listing_opf_post_effect()4449 sexp_list_item* sexp_tree::get_listing_opf_post_effect() {
4450 unsigned int i;
4451 sexp_list_item head;
4452
4453 SCP_vector<SCP_string> ppe_names;
4454 gr_get_post_process_effect_names(ppe_names);
4455 for (i = 0; i < ppe_names.size(); i++) {
4456 head.add_data_dup(ppe_names[i].c_str());
4457 }
4458 head.add_data_dup("lightshafts");
4459
4460 return head.next;
4461 }
4462
4463
get_listing_opf_turret_target_priorities()4464 sexp_list_item* sexp_tree::get_listing_opf_turret_target_priorities() {
4465 size_t t;
4466 sexp_list_item head;
4467
4468 for (t = 0; t < Ai_tp_list.size(); t++) {
4469 head.add_data(Ai_tp_list[t].name);
4470 }
4471
4472 return head.next;
4473 }
4474
get_listing_opf_armor_type()4475 sexp_list_item* sexp_tree::get_listing_opf_armor_type() {
4476 size_t t;
4477 sexp_list_item head;
4478 head.add_data(SEXP_NONE_STRING);
4479 for (t = 0; t < Armor_types.size(); t++) {
4480 head.add_data(Armor_types[t].GetNamePtr());
4481 }
4482 return head.next;
4483 }
4484
get_listing_opf_damage_type()4485 sexp_list_item* sexp_tree::get_listing_opf_damage_type() {
4486 size_t t;
4487 sexp_list_item head;
4488 head.add_data(SEXP_NONE_STRING);
4489 for (t = 0; t < Damage_types.size(); t++) {
4490 head.add_data(Damage_types[t].name);
4491 }
4492
4493 return head.next;
4494 }
4495
get_listing_opf_animation_type()4496 sexp_list_item* sexp_tree::get_listing_opf_animation_type() {
4497 sexp_list_item head;
4498
4499 for (const auto &animation_type_name: Animation_type_names) {
4500 head.add_data(animation_type_name.second);
4501 }
4502
4503 return head.next;
4504 }
4505
get_listing_opf_hud_elements()4506 sexp_list_item* sexp_tree::get_listing_opf_hud_elements() {
4507 sexp_list_item head;
4508 head.add_data("warpout");
4509
4510 return head.next;
4511 }
4512
get_listing_opf_weapon_banks()4513 sexp_list_item* sexp_tree::get_listing_opf_weapon_banks() {
4514 sexp_list_item head;
4515 head.add_data(SEXP_ALL_BANKS_STRING);
4516
4517 return head.next;
4518 }
4519
get_listing_opf_mission_moods()4520 sexp_list_item* sexp_tree::get_listing_opf_mission_moods() {
4521 sexp_list_item head;
4522 for (SCP_vector<SCP_string>::iterator iter = Builtin_moods.begin(); iter != Builtin_moods.end(); ++iter) {
4523 head.add_data_dup(iter->c_str());
4524 }
4525
4526 return head.next;
4527 }
4528
get_listing_opf_ship_flags()4529 sexp_list_item* sexp_tree::get_listing_opf_ship_flags() {
4530 int i;
4531 sexp_list_item head;
4532 // object flags
4533 for (i = 0; i < MAX_OBJECT_FLAG_NAMES; i++) {
4534 head.add_data_dup(Object_flag_names[i].flag_name);
4535 }
4536 // ship flags
4537 for (i = 0; i < MAX_SHIP_FLAG_NAMES; i++) {
4538 head.add_data_dup(Ship_flag_names[i].flag_name);
4539 }
4540 // ai flags
4541 for (i = 0; i < MAX_AI_FLAG_NAMES; i++) {
4542 head.add_data_dup(Ai_flag_names[i].flag_name);
4543 }
4544
4545 return head.next;
4546 }
4547
get_listing_opf_team_colors()4548 sexp_list_item* sexp_tree::get_listing_opf_team_colors() {
4549 sexp_list_item head;
4550 head.add_data("None");
4551 for (SCP_map<SCP_string, team_color>::iterator tcolor = Team_Colors.begin(); tcolor != Team_Colors.end();
4552 ++tcolor) {
4553 head.add_data_dup(tcolor->first.c_str());
4554 }
4555
4556 return head.next;
4557 }
4558
get_listing_opf_nebula_patterns()4559 sexp_list_item* sexp_tree::get_listing_opf_nebula_patterns() {
4560 sexp_list_item head;
4561
4562 head.add_data(SEXP_NONE_STRING);
4563
4564 for (int i = 0; i < MAX_NEB2_BITMAPS; i++) {
4565 if (strlen(Neb2_bitmap_filenames[i]) > 0) {
4566 head.add_data(Neb2_bitmap_filenames[i]);
4567 }
4568 }
4569
4570 return head.next;
4571 }
4572
get_listing_opf_game_snds()4573 sexp_list_item* sexp_tree::get_listing_opf_game_snds() {
4574 sexp_list_item head;
4575
4576 head.add_data(SEXP_NONE_STRING);
4577
4578 for (SCP_vector<game_snd>::iterator iter = Snds.begin(); iter != Snds.end(); ++iter) {
4579 if (!can_construe_as_integer(iter->name.c_str())) {
4580 head.add_data(iter->name.c_str());
4581 }
4582 }
4583
4584 return head.next;
4585 }
4586
get_listing_opf_fireball()4587 sexp_list_item *sexp_tree::get_listing_opf_fireball()
4588 {
4589 sexp_list_item head;
4590
4591 for (int i = 0; i < Num_fireball_types; ++i)
4592 {
4593 char *unique_id = Fireball_info[i].unique_id;
4594
4595 if (strlen(unique_id) > 0)
4596 head.add_data(unique_id);
4597 }
4598
4599 return head.next;
4600 }
4601
get_listing_opf_species()4602 sexp_list_item *sexp_tree::get_listing_opf_species() // NOLINT
4603 {
4604 sexp_list_item head;
4605
4606 for (auto &species : Species_info)
4607 head.add_data(species.species_name);
4608
4609 return head.next;
4610 }
4611
get_listing_opf_language()4612 sexp_list_item *sexp_tree::get_listing_opf_language() // NOLINT
4613 {
4614 sexp_list_item head;
4615
4616 for (auto &lang: Lcl_languages)
4617 head.add_data(lang.lang_name);
4618
4619 return head.next;
4620 }
4621
4622 // Deletes sexp_variable from sexp_tree.
4623 // resets tree to not include given variable, and resets text and type
delete_sexp_tree_variable(const char * var_name)4624 void sexp_tree::delete_sexp_tree_variable(const char* var_name) {
4625 char search_str[64];
4626 char replace_text[TOKEN_LENGTH];
4627
4628 sprintf(search_str, "%s(", var_name);
4629
4630 // store old item index
4631 int old_item_index = item_index;
4632
4633 for (uint idx = 0; idx < tree_nodes.size(); idx++) {
4634 if (tree_nodes[idx].type & SEXPT_VARIABLE) {
4635 if (strstr(tree_nodes[idx].text, search_str) != NULL) {
4636
4637 // check type is number or string
4638 Assert((tree_nodes[idx].type & SEXPT_NUMBER) || (tree_nodes[idx].type & SEXPT_STRING));
4639
4640 // reset type as not variable
4641 int type = tree_nodes[idx].type &= ~SEXPT_VARIABLE;
4642
4643 // reset text
4644 if (tree_nodes[idx].type & SEXPT_NUMBER) {
4645 strcpy_s(replace_text, "number");
4646 } else {
4647 strcpy_s(replace_text, "string");
4648 }
4649
4650 // set item_index and replace data
4651 setCurrentItemIndex(idx);
4652 replace_data(replace_text, type);
4653 }
4654 }
4655 }
4656
4657 // restore item_index
4658 setCurrentItemIndex(old_item_index);
4659 }
4660
4661
4662 // Modify sexp_tree for a change in sexp_variable (name, type, or default value)
modify_sexp_tree_variable(const char * old_name,int sexp_var_index)4663 void sexp_tree::modify_sexp_tree_variable(const char* old_name, int sexp_var_index) {
4664 char search_str[64];
4665 int type;
4666
4667 Assert(Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_SET);
4668 Assert((Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER)
4669 || (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING));
4670
4671 // Get type for sexp_tree node
4672 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
4673 type = (SEXPT_NUMBER | SEXPT_VALID);
4674 } else {
4675 type = (SEXPT_STRING | SEXPT_VALID);
4676 }
4677
4678 // store item index;
4679 int old_item_index = item_index;
4680
4681 // Search string in sexp_tree nodes
4682 sprintf(search_str, "%s(", old_name);
4683
4684 for (uint idx = 0; idx < tree_nodes.size(); idx++) {
4685 if (tree_nodes[idx].type & SEXPT_VARIABLE) {
4686 if (strstr(tree_nodes[idx].text, search_str) != NULL) {
4687 // temp set item_index
4688 item_index = idx;
4689
4690 // replace variable data
4691 replace_variable_data(sexp_var_index, (type | SEXPT_VARIABLE));
4692 }
4693 }
4694 }
4695
4696 // restore item_index
4697 item_index = old_item_index;
4698 }
4699
4700
4701 // convert from item_index to sexp_variable index, -1 if not
get_item_index_to_var_index()4702 int sexp_tree::get_item_index_to_var_index() {
4703 // check valid item index and node is a variable
4704 if ((item_index > 0) && (tree_nodes[item_index].type & SEXPT_VARIABLE)) {
4705
4706 return get_tree_name_to_sexp_variable_index(tree_nodes[item_index].text);
4707 } else {
4708 return -1;
4709 }
4710 }
4711
get_tree_name_to_sexp_variable_index(const char * tree_name)4712 int sexp_tree::get_tree_name_to_sexp_variable_index(const char* tree_name) {
4713 char var_name[TOKEN_LENGTH];
4714
4715 auto chars_to_copy = strcspn(tree_name, "(");
4716 Assert(chars_to_copy < TOKEN_LENGTH - 1);
4717
4718 // Copy up to '(' and add null termination
4719 strncpy(var_name, tree_name, chars_to_copy);
4720 var_name[chars_to_copy] = '\0';
4721
4722 // Look up index
4723 return get_index_sexp_variable_name(var_name);
4724 }
4725
get_variable_count(const char * var_name)4726 int sexp_tree::get_variable_count(const char* var_name) {
4727 uint idx;
4728 int count = 0;
4729 char compare_name[64];
4730
4731 // get name to compare
4732 strcpy_s(compare_name, var_name);
4733 strcat_s(compare_name, "(");
4734
4735 // look for compare name
4736 for (idx = 0; idx < tree_nodes.size(); idx++) {
4737 if (tree_nodes[idx].type & SEXPT_VARIABLE) {
4738 if (strstr(tree_nodes[idx].text, compare_name)) {
4739 count++;
4740 }
4741 }
4742 }
4743
4744 return count;
4745 }
4746
4747 // Returns the number of times a variable with this name has been used by player loadout
get_loadout_variable_count(int var_index)4748 int sexp_tree::get_loadout_variable_count(int var_index) {
4749 // we shouldn't be being passed the index of variables that do not exist
4750 Assert (var_index >= 0 && var_index < MAX_SEXP_VARIABLES);
4751
4752 int idx;
4753 int count = 0;
4754
4755 for (int i = 0; i < MAX_TVT_TEAMS; i++) {
4756 for (idx = 0; idx < Team_data[i].num_ship_choices; idx++) {
4757 if (!strcmp(Team_data[i].ship_list_variables[idx], Sexp_variables[var_index].variable_name)) {
4758 count++;
4759 }
4760
4761 if (!strcmp(Team_data[i].ship_count_variables[idx], Sexp_variables[var_index].variable_name)) {
4762 count++;
4763 }
4764 }
4765
4766 for (idx = 0; idx < Team_data[i].num_weapon_choices; idx++) {
4767 if (!strcmp(Team_data[i].weaponry_pool_variable[idx], Sexp_variables[var_index].variable_name)) {
4768 count++;
4769 }
4770 if (!strcmp(Team_data[i].weaponry_amount_variable[idx], Sexp_variables[var_index].variable_name)) {
4771 count++;
4772 }
4773 }
4774 }
4775
4776 return count;
4777 }
initializeEditor(::fso::fred::Editor * edit,SexpTreeEditorInterface * editorInterface)4778 void sexp_tree::initializeEditor(::fso::fred::Editor* edit, SexpTreeEditorInterface* editorInterface) {
4779 if (editorInterface == nullptr) {
4780 // If there is no special interface then we supply the default implementation
4781 _owned_interface.reset(new SexpTreeEditorInterface());
4782 editorInterface = _owned_interface.get();
4783 }
4784
4785 _editor = edit;
4786 _interface = editorInterface;
4787 }
customMenuHandler(const QPoint & pos)4788 void sexp_tree::customMenuHandler(const QPoint& pos) {
4789 QTreeWidgetItem* h = this->itemAt(pos);
4790
4791 if (h == nullptr) {
4792 return;
4793 }
4794
4795 auto menu = buildContextMenu(h);
4796
4797 menu->exec(mapToGlobal(pos));
4798 }
buildContextMenu(QTreeWidgetItem * h)4799 std::unique_ptr<QMenu> sexp_tree::buildContextMenu(QTreeWidgetItem* h) {
4800 int i, j, z, count, op, add_type, replace_type, type, subcategory_id;
4801 sexp_list_item* list;
4802
4803 add_instance = replace_instance = -1;
4804 Assert(Operators.size() <= MAX_OPERATORS);
4805 Assert((int) op_menu.size() < MAX_OP_MENUS);
4806 Assert((int) op_submenu.size() < MAX_SUBMENUS);
4807
4808 std::unique_ptr<QMenu> popup_menu(new QMenu(tr("Edit SEXP tree")));
4809
4810 auto delete_act =
4811 popup_menu->addAction(tr("&Delete Item"), this, [this]() { deleteActionHandler(); }, QKeySequence::Delete);
4812 auto edit_data_act = popup_menu->addAction(tr("&Edit Data"), this, [this]() { editDataActionHandler(); });
4813 popup_menu->addAction(tr("Expand All"), this, [this]() { expand_branch(currentItem()); });
4814
4815 popup_menu->addSection(tr("Copy operations"));
4816 auto cut_act = popup_menu->addAction(tr("Cut"), this, [this]() { cutActionHandler(); }, QKeySequence::Cut);
4817 cut_act->setEnabled(false);
4818 auto copy_act = popup_menu->addAction(tr("Copy"), this, [this]() { copyActionHandler(); }, QKeySequence::Copy);
4819 auto paste_act = popup_menu->addAction(tr("Paste"), this, [this]() { pasteActionHandler(); }, QKeySequence::Paste);
4820 paste_act->setEnabled(false);
4821
4822 popup_menu->addSection(tr("Add"));
4823 auto add_op_menu = popup_menu->addMenu(tr("Add Operator"));
4824
4825 auto add_data_menu = popup_menu->addMenu(tr("Add Data"));
4826 auto add_number_act = add_data_menu->addAction(tr("Number"), this, [this]() { addNumberDataHandler(); });
4827 add_number_act->setEnabled(false);
4828 auto add_string_act = add_data_menu->addAction(tr("String"), this, [this]() { addStringDataHandler(); });
4829 add_string_act->setEnabled(false);
4830 add_data_menu->addSeparator();
4831
4832 popup_menu->addSeparator();
4833 auto add_paste_act = popup_menu->addAction(tr("Add Paste"), this, [this]() { addPasteActionHandler(); });
4834 add_paste_act->setEnabled(false);
4835 popup_menu->addSeparator();
4836
4837 auto insert_op_menu = popup_menu->addMenu(tr("Insert Operator"));
4838 popup_menu->addSeparator();
4839
4840 auto replace_op_menu = popup_menu->addMenu(tr("Replace Operator"));
4841
4842 auto replace_data_menu = popup_menu->addMenu(tr("Replace Data"));
4843 auto replace_number_act = replace_data_menu->addAction(tr("Number"), this, []() {});
4844 replace_number_act->setEnabled(false);
4845 auto replace_string_act = replace_data_menu->addAction(tr("String"), this, []() {});
4846 replace_string_act->setEnabled(false);
4847 replace_data_menu->addSeparator();
4848
4849 popup_menu->addSection("Variables");
4850
4851 popup_menu->addAction(tr("Add Variable"), this, []() {});
4852 auto modify_variable_act = popup_menu->addAction(tr("Modify Variable"), this, []() {});
4853
4854 auto replace_variable_menu = popup_menu->addMenu(tr("Replace Variable"));
4855
4856 update_help(h);
4857 //SelectDropTarget(h); // WTF: Why was this here???
4858
4859 // get item_index
4860 item_index = -1;
4861 for (i = 0; i < (int) tree_nodes.size(); i++) {
4862 if (tree_nodes[i].handle == h) {
4863 setCurrentItemIndex(i);
4864 break;
4865 }
4866 }
4867
4868 // check not root (-1)
4869 if (item_index >= 0) {
4870 // get type of sexp_tree item clicked on
4871 type = get_type(h);
4872
4873 int parent = tree_nodes[item_index].parent;
4874 if (parent >= 0) {
4875 op = get_operator_index(tree_nodes[parent].text);
4876 Assert(op >= 0);
4877 int first_arg = tree_nodes[parent].child;
4878
4879 // get arg count of item to replace
4880 Replace_count = 0;
4881 int temp = first_arg;
4882 while (temp != item_index) {
4883 Replace_count++;
4884 temp = tree_nodes[temp].next;
4885
4886 // DB - added 3/4/99
4887 if (temp == -1) {
4888 break;
4889 }
4890 }
4891
4892 int op_type = query_operator_argument_type(op, Replace_count); // check argument type at this position
4893
4894 // special case don't allow replace data for variable names
4895 // Goober5000 - why? the only place this happens is when replacing the ambiguous argument in
4896 // modify-variable with a variable, which seems legal enough.
4897 //if (op_type != OPF_AMBIGUOUS) {
4898
4899 // Goober5000 - given the above, we have to figure out what type this stands for
4900 if (op_type == OPF_AMBIGUOUS) {
4901 int modify_type = get_modify_variable_type(parent);
4902 if (modify_type == OPF_NUMBER) {
4903 type = SEXPT_NUMBER;
4904 } else if (modify_type == OPF_AMBIGUOUS) {
4905 type = SEXPT_STRING;
4906 } else {
4907 Int3();
4908 type = tree_nodes[first_arg].type;
4909 }
4910 }
4911
4912 // Goober5000 - certain types accept both integers and a list of strings
4913 if (op_type == OPF_GAME_SND || op_type == OPF_FIREBALL || op_type == OPF_WEAPON_BANK_NUMBER) {
4914 type = SEXPT_NUMBER | SEXPT_STRING;
4915 }
4916
4917 if ((type & SEXPT_STRING) || (type & SEXPT_NUMBER)) {
4918
4919 int max_sexp_vars = MAX_SEXP_VARIABLES;
4920 // prevent collisions in id numbers: ID_VARIABLE_MENU + 512 = ID_ADD_MENU
4921 Assert(max_sexp_vars < 512);
4922
4923 for (int idx = 0; idx < max_sexp_vars; idx++) {
4924 if (Sexp_variables[idx].type & SEXP_VARIABLE_SET) {
4925 // skip block variables
4926 if (Sexp_variables[idx].type & SEXP_VARIABLE_BLOCK) {
4927 continue;
4928 }
4929
4930 auto disabled = true;
4931 // maybe gray flag MF_GRAYED
4932
4933 // get type -- gray "string" or number accordingly
4934 if (type & SEXPT_STRING) {
4935 if (Sexp_variables[idx].type & SEXP_VARIABLE_STRING) {
4936 disabled = false;
4937 }
4938 }
4939 if (type & SEXPT_NUMBER) {
4940 if (Sexp_variables[idx].type & SEXP_VARIABLE_NUMBER) {
4941 disabled = false;
4942 }
4943 }
4944
4945 // if modify-variable and changing variable, enable all variables
4946 if (op_type == OPF_VARIABLE_NAME) {
4947 Modify_variable = 1;
4948 disabled = false;
4949 } else {
4950 Modify_variable = 0;
4951 }
4952
4953 // enable navsystem always
4954 if (op_type == OPF_NAV_POINT) {
4955 disabled = false;
4956 }
4957
4958 char buf[128];
4959 // append list of variable names and values
4960 // set id as ID_VARIABLE_MENU + idx
4961 sprintf(buf, "%s (%s)", Sexp_variables[idx].variable_name, Sexp_variables[idx].text);
4962
4963 auto action = replace_variable_menu->addAction(QString::fromUtf8(buf),
4964 this,
4965 [this, idx]() { handleReplaceVariableAction(idx); });
4966 action->setEnabled(!disabled);
4967 }
4968 }
4969 }
4970 //}
4971 }
4972 }
4973
4974 // can't modify if no variables
4975 modify_variable_act->setEnabled(sexp_variable_count() > 0);
4976
4977 // add popup menus for all the operator categories
4978 QMenu* add_op_submenu[MAX_OP_MENUS];
4979 QMenu* replace_op_submenu[MAX_OP_MENUS];
4980 QMenu* insert_op_submenu[MAX_OP_MENUS];
4981 for (i = 0; i < (int) op_menu.size(); i++) {
4982 add_op_submenu[i] = add_op_menu->addMenu(QString::fromStdString(op_menu[i].name.c_str()));
4983 replace_op_submenu[i] = replace_op_menu->addMenu(QString::fromStdString(op_menu[i].name.c_str()));
4984 insert_op_submenu[i] = insert_op_menu->addMenu(QString::fromStdString(op_menu[i].name));
4985 }
4986
4987 // add all the submenu items first
4988 QMenu* add_op_subcategory_menu[MAX_SUBMENUS];
4989 QMenu* replace_op_subcategory_menu[MAX_SUBMENUS];
4990 QMenu* insert_op_subcategory_menu[MAX_SUBMENUS];
4991 for (i = 0; i < (int) op_submenu.size(); i++) {
4992 for (j = 0; j < (int) op_menu.size(); j++) {
4993 if (op_menu[j].id == category_of_subcategory(op_submenu[i].id)) {
4994 add_op_subcategory_menu[i] = add_op_submenu[j]->addMenu(QString::fromStdString(op_submenu[i].name));
4995 replace_op_subcategory_menu[i] =
4996 replace_op_submenu[j]->addMenu(QString::fromStdString(op_submenu[i].name));
4997 insert_op_subcategory_menu[i] =
4998 insert_op_submenu[j]->addMenu(QString::fromStdString(op_submenu[i].name));
4999 break; // only 1 category valid
5000 }
5001 }
5002 }
5003
5004 // The MFC code could use some internal WinAPI IDs for keeping this mapping but that is not available in Qt so we
5005 // need to do this ourself. This could be improved by applying the actions for the operator actions when the action
5006 // is added.
5007 std::unordered_map<int, QAction*> operator_action_mapping;
5008
5009 // add operator menu items to the various CATEGORY submenus they belong in
5010 for (i = 0; i < (int) Operators.size(); i++) {
5011 // add only if it is not in a subcategory
5012 subcategory_id = get_subcategory(Operators[i].value);
5013 if (subcategory_id == -1) {
5014 // put it in the appropriate menu
5015 for (j = 0; j < (int) op_menu.size(); j++) {
5016 if (op_menu[j].id == get_category(Operators[i].value)) {
5017 switch (Operators[i].value) {
5018 // Commented out by Goober5000 to allow these operators to be selectable
5019 /*#ifdef NDEBUG
5020 // various campaign operators
5021 case OP_WAS_PROMOTION_GRANTED:
5022 case OP_WAS_MEDAL_GRANTED:
5023 case OP_GRANT_PROMOTION:
5024 case OP_GRANT_MEDAL:
5025 case OP_TECH_ADD_SHIP:
5026 case OP_TECH_ADD_WEAPON:
5027 case OP_TECH_ADD_INTEL_XSTR:
5028 case OP_TECH_REMOVE_INTEL_XSTR:
5029 case OP_TECH_RESET_TO_DEFAULT:
5030 #endif*/
5031 // unlike the above operators, these are deprecated
5032 case OP_HITS_LEFT_SUBSYSTEM:
5033 case OP_CUTSCENES_SHOW_SUBTITLE:
5034 case OP_ORDER:
5035 case OP_TECH_ADD_INTEL:
5036 case OP_TECH_REMOVE_INTEL:
5037 case OP_HUD_GAUGE_SET_ACTIVE:
5038 case OP_HUD_ACTIVATE_GAUGE_TYPE:
5039 case OP_JETTISON_CARGO_DELAY:
5040 case OP_STRING_CONCATENATE:
5041 case OP_SET_OBJECT_SPEED_X:
5042 case OP_SET_OBJECT_SPEED_Y:
5043 case OP_SET_OBJECT_SPEED_Z:
5044 case OP_DISTANCE:
5045 case OP_SCRIPT_EVAL:
5046 j = (int) op_menu.size(); // don't allow these operators to be visible
5047 break;
5048 }
5049
5050 if (j < (int) op_menu.size()) {
5051 auto add_act =
5052 add_op_submenu[j]->addAction(QString::fromStdString(Operators[i].text), this, [this, i]() {
5053 add_or_replace_operator(i, 0);
5054 });
5055 add_act->setEnabled(false);
5056 operator_action_mapping.insert(std::make_pair(Operators[i].value, add_act));
5057
5058 auto replace_act = replace_op_submenu[j]->addAction(QString::fromStdString(Operators[i].text),
5059 this,
5060 [this, i]() {
5061 add_or_replace_operator(i, 1);
5062 });
5063 replace_act->setEnabled(false);
5064 operator_action_mapping.insert(std::make_pair(Operators[i].value | OP_REPLACE_FLAG,
5065 replace_act));
5066
5067 auto insert_act = insert_op_submenu[j]->addAction(QString::fromStdString(Operators[i].text),
5068 this,
5069 [this, i]() {
5070 insertOperatorAction(i);
5071 });
5072 insert_act->setEnabled(true);
5073 operator_action_mapping.insert(std::make_pair(Operators[i].value | OP_INSERT_FLAG, insert_act));
5074 }
5075
5076 break; // only 1 category valid
5077 }
5078 }
5079 }
5080 // if it is in a subcategory, handle it
5081 else {
5082 // put it in the appropriate submenu
5083 for (j = 0; j < (int) op_submenu.size(); j++) {
5084 if (op_submenu[j].id == subcategory_id) {
5085 switch (Operators[i].value) {
5086 // Commented out by Goober5000 to allow these operators to be selectable
5087 /*#ifdef NDEBUG
5088 // various campaign operators
5089 case OP_WAS_PROMOTION_GRANTED:
5090 case OP_WAS_MEDAL_GRANTED:
5091 case OP_GRANT_PROMOTION:
5092 case OP_GRANT_MEDAL:
5093 case OP_TECH_ADD_SHIP:
5094 case OP_TECH_ADD_WEAPON:
5095 case OP_TECH_ADD_INTEL_XSTR:
5096 case OP_TECH_REMOVE_INTEL_XSTR:
5097 case OP_TECH_RESET_TO_DEFAULT:
5098 #endif*/
5099 // unlike the above operators, these are deprecated
5100 case OP_HITS_LEFT_SUBSYSTEM:
5101 case OP_CUTSCENES_SHOW_SUBTITLE:
5102 case OP_ORDER:
5103 case OP_TECH_ADD_INTEL:
5104 case OP_TECH_REMOVE_INTEL:
5105 case OP_HUD_GAUGE_SET_ACTIVE:
5106 case OP_HUD_ACTIVATE_GAUGE_TYPE:
5107 case OP_JETTISON_CARGO_DELAY:
5108 case OP_STRING_CONCATENATE:
5109 case OP_SET_OBJECT_SPEED_X:
5110 case OP_SET_OBJECT_SPEED_Y:
5111 case OP_SET_OBJECT_SPEED_Z:
5112 case OP_DISTANCE:
5113 case OP_SCRIPT_EVAL:
5114 j = (int) op_submenu.size(); // don't allow these operators to be visible
5115 break;
5116 }
5117
5118 if (j < (int) op_submenu.size()) {
5119 auto add_act = add_op_subcategory_menu[j]->addAction(QString::fromStdString(Operators[i].text),
5120 this,
5121 [this, i]() {
5122 add_or_replace_operator(i, 0);
5123 });
5124 add_act->setEnabled(false);
5125 operator_action_mapping.insert(std::make_pair(Operators[i].value, add_act));
5126
5127 auto replace_act =
5128 replace_op_subcategory_menu[j]->addAction(QString::fromStdString(Operators[i].text),
5129 this,
5130 [this, i]() {
5131 add_or_replace_operator(i, 1);
5132 });
5133 replace_act->setEnabled(false);
5134 operator_action_mapping.insert(std::make_pair(Operators[i].value | OP_REPLACE_FLAG,
5135 replace_act));
5136
5137 auto insert_act =
5138 insert_op_subcategory_menu[j]->addAction(QString::fromStdString(Operators[i].text),
5139 this,
5140 [this, i]() {
5141 insertOperatorAction(i);
5142 });
5143 insert_act->setEnabled(true);
5144 operator_action_mapping.insert(std::make_pair(Operators[i].value | OP_INSERT_FLAG, insert_act));
5145 }
5146
5147 break; // only 1 subcategory valid
5148 }
5149 }
5150 }
5151 }
5152
5153 // find local index (i) of current item (from its handle)
5154 for (i = 0; i < (int) tree_nodes.size(); i++) {
5155 if (tree_nodes[i].handle == h) {
5156 break;
5157 }
5158 }
5159
5160 // special case: item is a ROOT node, and a label that can be edited (not an item in the sexp tree)
5161 if ((item_index == -1) && _interface->getFlags()[TreeFlags::LabeledRoot]) {
5162 edit_data_act->setEnabled(_interface->getFlags()[TreeFlags::RootEditable]);
5163
5164 // disable copy, insert op
5165 copy_act->setEnabled(false);
5166 insert_op_menu->setEnabled(false);
5167 /*
5168 for (j = 0; j < (int) Operators.size(); j++) {
5169 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
5170 }
5171 */
5172
5173 util::propagate_disabled_status(popup_menu.get());
5174 return popup_menu;
5175 }
5176
5177 Assert(item_index != -1); // handle not found, which should be impossible.
5178 if (!(tree_nodes[item_index].flags & EDITABLE)) {
5179 edit_data_act->setEnabled(false);
5180 }
5181
5182 if (tree_nodes[item_index].parent == -1) { // root node
5183 delete_act->setEnabled(false); // can't delete the root item.
5184 }
5185
5186 /* if ((tree_nodes[item_index].flags & OPERAND) && (tree_nodes[item_index].flags & EDITABLE)) // expandable?
5187 menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
5188
5189 z = tree_nodes[item_index].child;
5190 if (z != -1 && tree_nodes[z].next == -1 && tree_nodes[z].child == -1)
5191 menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
5192
5193 z = tree_nodes[tree_nodes[item_index].parent].child;
5194 if (z != -1 && tree_nodes[z].next == -1 && tree_nodes[z].child == -1)
5195 menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);*/
5196
5197 // change enabled status of 'add' type menu options.
5198 add_type = 0;
5199 if (tree_nodes[item_index].flags & OPERAND) {
5200 add_type = OPR_STRING;
5201 int child = tree_nodes[item_index].child;
5202 Add_count = count_args(child);
5203 op = get_operator_index(tree_nodes[item_index].text);
5204 Assert(op >= 0);
5205
5206 // get listing of valid argument values and add to menus
5207 type = query_operator_argument_type(op, Add_count);
5208 list = get_listing_opf(type, item_index, Add_count);
5209 if (list) {
5210 sexp_list_item* ptr;
5211
5212 int data_idx = 0;
5213 ptr = list;
5214 while (ptr) {
5215 if (ptr->op >= 0) {
5216 // enable operators with correct return type
5217 auto iter = operator_action_mapping.find(Operators[ptr->op].value);
5218 if (iter != operator_action_mapping.end()) {
5219 iter->second->setEnabled(true);
5220 }
5221 } else {
5222 // add data
5223 add_data_menu->addAction(QString::fromStdString(ptr->text),
5224 this,
5225 [this, data_idx]() { addReplaceTypedDataHandler(data_idx, false); });
5226 }
5227
5228 data_idx++;
5229 ptr = ptr->next;
5230 }
5231 }
5232
5233 // special handling for the non-string formats
5234 if (type == OPF_NONE) { // an argument can't be added
5235 add_type = 0;
5236
5237 } else if (type == OPF_NULL) { // arguments with no return values
5238 add_type = OPR_NULL;
5239
5240 // Goober5000
5241 } else if (type == OPF_FLEXIBLE_ARGUMENT) {
5242 add_type = OPR_FLEXIBLE_ARGUMENT;
5243
5244 } else if (type == OPF_NUMBER) { // takes numbers
5245 add_type = OPR_NUMBER;
5246 add_number_act->setEnabled(true);
5247 } else if (type == OPF_POSITIVE) { // takes non-negative numbers
5248 add_type = OPR_POSITIVE;
5249 add_number_act->setEnabled(true);
5250 } else if (type == OPF_BOOL) { // takes true/false bool values
5251 add_type = OPR_BOOL;
5252
5253 } else if (type == OPF_AI_GOAL) {
5254 add_type = OPR_AI_GOAL;
5255 }
5256
5257 // add_type unchanged from above
5258 if (add_type == OPR_STRING) {
5259 add_string_act->setEnabled(true);
5260 }
5261
5262 list->destroy();
5263 }
5264
5265 // disable operators that do not have arguments available
5266 for (j = 0; j < (int) Operators.size(); j++) {
5267 if (!query_default_argument_available(j)) {
5268 auto iter = operator_action_mapping.find(Operators[j].value);
5269 if (iter != operator_action_mapping.end()) {
5270 iter->second->setEnabled(false);
5271 }
5272 }
5273 }
5274
5275
5276 // change enabled status of 'replace' type menu options.
5277 replace_type = 0;
5278 int parent = tree_nodes[item_index].parent;
5279 if (parent >= 0) {
5280 replace_type = OPR_STRING;
5281 op = get_operator_index(tree_nodes[parent].text);
5282 Assert(op >= 0);
5283 int first_arg = tree_nodes[parent].child;
5284 count = count_args(tree_nodes[parent].child);
5285
5286 // already at minimum number of arguments?
5287 if (count <= Operators[op].min) {
5288 delete_act->setEnabled(false);
5289 }
5290
5291 // get arg count of item to replace
5292 Replace_count = 0;
5293 int temp = first_arg;
5294 while (temp != item_index) {
5295 Replace_count++;
5296 temp = tree_nodes[temp].next;
5297
5298 // DB - added 3/4/99
5299 if (temp == -1) {
5300 break;
5301 }
5302 }
5303
5304 // maybe gray delete
5305 for (i = Replace_count + 1; i < count; i++) {
5306 if (query_operator_argument_type(op, i - 1) != query_operator_argument_type(op, i)) {
5307 delete_act->setEnabled(false);
5308 break;
5309 }
5310 }
5311
5312 type = query_operator_argument_type(op, Replace_count); // check argument type at this position
5313
5314 // special case reset type for ambiguous
5315 if (type == OPF_AMBIGUOUS) {
5316 type = get_modify_variable_type(parent);
5317 }
5318
5319 list = get_listing_opf(type, parent, Replace_count);
5320
5321 // special case don't allow replace data for variable names
5322 if ((type != OPF_VARIABLE_NAME) && list) {
5323 sexp_list_item* ptr;
5324
5325 int data_idx = 0;
5326 ptr = list;
5327 while (ptr) {
5328 if (ptr->op >= 0) {
5329 auto iter = operator_action_mapping.find(Operators[ptr->op].value | OP_REPLACE_FLAG);
5330 if (iter != operator_action_mapping.end()) {
5331 iter->second->setEnabled(true);
5332 }
5333 } else {
5334 replace_data_menu->addAction(QString::fromStdString(ptr->text),
5335 this,
5336 [this, data_idx]() { addReplaceTypedDataHandler(data_idx, true); });
5337 }
5338
5339 data_idx++;
5340 ptr = ptr->next;
5341 }
5342 }
5343
5344 if (type == OPF_NONE) { // takes no arguments
5345 replace_type = 0;
5346
5347 } else if (type == OPF_NUMBER) { // takes numbers
5348 replace_type = OPR_NUMBER;
5349 replace_number_act->setEnabled(true);
5350 } else if (type == OPF_POSITIVE) { // takes non-negative numbers
5351 replace_type = OPR_POSITIVE;
5352 replace_number_act->setEnabled(true);
5353 } else if (type == OPF_BOOL) { // takes true/false bool values
5354 replace_type = OPR_BOOL;
5355
5356 } else if (type == OPF_NULL) { // takes operator that doesn't return a value
5357 replace_type = OPR_NULL;
5358 } else if (type == OPF_AI_GOAL) {
5359 replace_type = OPR_AI_GOAL;
5360 }
5361
5362 // Goober5000
5363 else if (type == OPF_FLEXIBLE_ARGUMENT) {
5364 replace_type = OPR_FLEXIBLE_ARGUMENT;
5365 }
5366 // Goober5000
5367 else if (type == OPF_GAME_SND || type == OPF_FIREBALL || type == OPF_WEAPON_BANK_NUMBER) {
5368 // even though these default to strings, we allow replacing them with index values
5369 replace_type = OPR_POSITIVE;
5370 replace_number_act->setEnabled(true);
5371 }
5372
5373 // default to string
5374 if (replace_type == OPR_STRING) {
5375 replace_string_act->setEnabled(true);
5376 }
5377
5378 // modify string or number if (modify_variable)
5379 if (Operators[op].value == OP_MODIFY_VARIABLE) {
5380 int modify_type = get_modify_variable_type(parent);
5381
5382 if (modify_type == OPF_NUMBER) {
5383 replace_number_act->setEnabled(true);
5384 replace_string_act->setEnabled(false);
5385 }
5386 // no change for string type
5387 } else if (Operators[op].value == OP_SET_VARIABLE_BY_INDEX) {
5388 // it depends on which argument we are modifying
5389 // first argument is always a number
5390 if (Replace_count == 0) {
5391 replace_number_act->setEnabled(true);
5392 replace_string_act->setEnabled(false);
5393 }
5394 // second argument could be anything
5395 else {
5396 int modify_type = get_modify_variable_type(parent);
5397
5398 if (modify_type == OPF_NUMBER) {
5399 replace_number_act->setEnabled(true);
5400 replace_string_act->setEnabled(false);
5401 }
5402 // no change for string type
5403 }
5404 }
5405
5406 list->destroy();
5407
5408 } else { // top node, so should be a Boolean type.
5409 replace_type = _interface->getRootReturnType();
5410 for (j = 0; j < (int) Operators.size(); j++) {
5411 if (query_operator_return_type(j) == replace_type) {
5412 auto iter = operator_action_mapping.find(Operators[j].value | OP_REPLACE_FLAG);
5413 if (iter != operator_action_mapping.end()) {
5414 iter->second->setEnabled(true);
5415 }
5416 }
5417 }
5418 }
5419
5420 // disable operators that do not have arguments available
5421 for (j = 0; j < (int) Operators.size(); j++) {
5422 if (!query_default_argument_available(j)) {
5423 auto iter = operator_action_mapping.find(Operators[j].value | OP_REPLACE_FLAG);
5424 if (iter != operator_action_mapping.end()) {
5425 iter->second->setEnabled(false);
5426 }
5427 }
5428 }
5429
5430
5431 // change enabled status of 'insert' type menu options.
5432 z = tree_nodes[item_index].parent;
5433 Assert(z >= -1);
5434 if (z != -1) {
5435 op = get_operator_index(tree_nodes[z].text);
5436 Assert(op != -1);
5437 j = tree_nodes[z].child;
5438 count = 0;
5439 while (j != item_index) {
5440 count++;
5441 j = tree_nodes[j].next;
5442 }
5443
5444 type = query_operator_argument_type(op, count); // check argument type at this position
5445
5446 } else {
5447 type = _interface->getRootReturnType();
5448 }
5449
5450 for (j = 0; j < (int) Operators.size(); j++) {
5451 z = query_operator_return_type(j);
5452 if (!sexp_query_type_match(type, z) || (Operators[j].min < 1)) {
5453 auto iter = operator_action_mapping.find(Operators[j].value | OP_INSERT_FLAG);
5454 if (iter != operator_action_mapping.end()) {
5455 iter->second->setEnabled(false);
5456 }
5457 }
5458
5459 z = query_operator_argument_type(j, 0);
5460 if ((type == OPF_NUMBER) && (z == OPF_POSITIVE)) {
5461 z = OPF_NUMBER;
5462 }
5463
5464 // Goober5000's number hack
5465 if ((type == OPF_POSITIVE) && (z == OPF_NUMBER)) {
5466 z = OPF_POSITIVE;
5467 }
5468
5469 if (z != type) {
5470 auto iter = operator_action_mapping.find(Operators[j].value | OP_INSERT_FLAG);
5471 if (iter != operator_action_mapping.end()) {
5472 iter->second->setEnabled(false);
5473 }
5474 }
5475 }
5476
5477 // disable operators that do not have arguments available
5478 for (j = 0; j < (int) Operators.size(); j++) {
5479 if (!query_default_argument_available(j)) {
5480 auto iter = operator_action_mapping.find(Operators[j].value | OP_INSERT_FLAG);
5481 if (iter != operator_action_mapping.end()) {
5482 iter->second->setEnabled(false);
5483 }
5484 }
5485 }
5486
5487
5488 // disable non campaign operators if in campaign mode
5489 for (j = 0; j < (int) Operators.size(); j++) {
5490 z = 0;
5491 if (_interface->requireCampaignOperators()) {
5492 if (Operators[j].value & OP_NONCAMPAIGN_FLAG) {
5493 z = 1;
5494 }
5495
5496 } else {
5497 if (Operators[j].value & OP_CAMPAIGN_ONLY_FLAG) {
5498 z = 1;
5499 }
5500 }
5501
5502 if (z) {
5503 auto iter = operator_action_mapping.find(Operators[j].value);
5504 if (iter != operator_action_mapping.end()) {
5505 iter->second->setEnabled(false);
5506 }
5507 iter = operator_action_mapping.find(Operators[j].value | OP_REPLACE_FLAG);
5508 if (iter != operator_action_mapping.end()) {
5509 iter->second->setEnabled(false);
5510 }
5511 iter = operator_action_mapping.find(Operators[j].value | OP_INSERT_FLAG);
5512 if (iter != operator_action_mapping.end()) {
5513 iter->second->setEnabled(false);
5514 }
5515 }
5516 }
5517
5518 if ((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)) {
5519 Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
5520
5521 if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
5522 j = get_operator_const(CTEXT(Sexp_clipboard));
5523 Assert(j);
5524 z = query_operator_return_type(j);
5525
5526 if ((z == OPR_POSITIVE) && (replace_type == OPR_NUMBER)) {
5527 z = OPR_NUMBER;
5528 }
5529
5530 // Goober5000's number hack
5531 if ((z == OPR_NUMBER) && (replace_type == OPR_POSITIVE)) {
5532 z = OPR_POSITIVE;
5533 }
5534
5535 if (replace_type == z) {
5536 paste_act->setEnabled(true);
5537 }
5538
5539 z = query_operator_return_type(j);
5540 if ((z == OPR_POSITIVE) && (add_type == OPR_NUMBER)) {
5541 z = OPR_NUMBER;
5542 }
5543
5544 if (add_type == z) {
5545 add_paste_act->setEnabled(true);
5546 }
5547
5548 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
5549 if ((replace_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1)) {
5550 edit_data_act->setEnabled(true);
5551 } else if (replace_type == OPR_NUMBER) {
5552 edit_data_act->setEnabled(true);
5553 }
5554
5555 if ((add_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1)) {
5556 add_paste_act->setEnabled(true);
5557 } else if (add_type == OPR_NUMBER) {
5558 add_paste_act->setEnabled(true);
5559 }
5560
5561 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
5562 if (replace_type == OPR_STRING) {
5563 edit_data_act->setEnabled(true);
5564 }
5565
5566 if (add_type == OPR_STRING) {
5567 add_paste_act->setEnabled(true);
5568 }
5569
5570 } else
5571 Int3(); // unknown and/or invalid sexp type
5572 }
5573
5574 if (delete_act->isEnabled()) {
5575 cut_act->setEnabled(true);
5576 }
5577
5578 util::propagate_disabled_status(popup_menu.get());
5579 return popup_menu;
5580 }
cutActionHandler()5581 void sexp_tree::cutActionHandler() {
5582 if (Sexp_clipboard != -1) {
5583 sexp_unmark_persistent(Sexp_clipboard);
5584 free_sexp2(Sexp_clipboard);
5585 }
5586
5587 Sexp_clipboard = save_branch(item_index, 1);
5588 sexp_mark_persistent(Sexp_clipboard);
5589
5590 // fall through to ID_DELETE case.
5591 deleteActionHandler();
5592 }
deleteActionHandler()5593 void sexp_tree::deleteActionHandler() {
5594 if (currentItem() == nullptr) {
5595 return;
5596 }
5597
5598 if (_interface->getFlags()[TreeFlags::RootDeletable] && (item_index == -1)) {
5599 auto item = currentItem();
5600 item_index = item->data(0, FormulaDataRole).toInt();
5601
5602 rootNodeDeleted(item_index);
5603
5604 free_node2(item_index);
5605 delete item;
5606 modified();
5607 return;
5608 }
5609
5610 Assert(item_index >= 0);
5611 auto h_parent = currentItem()->parent();
5612 auto parent = tree_nodes[item_index].parent;
5613
5614 Assert(parent != -1 && tree_nodes[parent].handle == h_parent);
5615 free_node(item_index);
5616 delete currentItem();
5617
5618 modified();
5619 }
editDataActionHandler()5620 void sexp_tree::editDataActionHandler() {
5621 beginItemEdit(currentItem());
5622 }
handleItemChange(QTreeWidgetItem * item,int)5623 void sexp_tree::handleItemChange(QTreeWidgetItem* item, int /*column*/) {
5624 if (!_currently_editing) {
5625 return;
5626 }
5627 _currently_editing = false;
5628
5629 auto str = item->text(0);
5630 bool update_node = true;
5631 uint node;
5632
5633 if (str.isEmpty()) {
5634 return;
5635 }
5636
5637 for (node = 0; node < tree_nodes.size(); node++) {
5638 if (tree_nodes[node].handle == item) {
5639 break;
5640 }
5641 }
5642
5643 if (node == tree_nodes.size()) {
5644 setCurrentItemIndex(qvariant_cast<int>(item->data(0, FormulaDataRole)));
5645
5646 rootNodeRenamed(item_index);
5647
5648 return;
5649 }
5650
5651 Assert(node < tree_nodes.size());
5652 if (tree_nodes[node].type & SEXPT_OPERATOR) {
5653 auto op = match_closest_operator(str.toStdString(), node);
5654 if (op.empty()) {
5655 return;
5656 } // Goober5000 - avoids crashing
5657
5658 // use the text of the operator we found
5659 str = QString::fromStdString(op);
5660 item->setText(0, str);
5661
5662 setCurrentItemIndex(node);
5663 int op_num = get_operator_index(op.c_str());
5664 if (op_num >= 0) {
5665 add_or_replace_operator(op_num, 1);
5666 } else {
5667 update_node = false;
5668 }
5669 }
5670 // gotta sidestep Goober5000's number hack and check entries are actually positive.
5671 else if (tree_nodes[node].type & SEXPT_NUMBER) {
5672 if (query_node_argument_type(node) == OPF_POSITIVE) {
5673 int val = str.toInt();
5674 if (val < 0) {
5675 QMessageBox::critical(this, "Invalid Number", "Can not enter a negative value");
5676 update_node = false;
5677 }
5678 }
5679 }
5680
5681 // Error checking would not hurt here
5682 auto len = str.size();
5683 if (len >= TOKEN_LENGTH) {
5684 len = TOKEN_LENGTH - 1;
5685 }
5686
5687 if (update_node) {
5688 modified();
5689 strncpy(tree_nodes[node].text, str.toStdString().c_str(), len);
5690 tree_nodes[node].text[len] = 0;
5691
5692 // let's make sure we aren't introducing any invalid characters, per Mantis #2893
5693 lcl_fred_replace_stuff(tree_nodes[node].text, TOKEN_LENGTH - 1);
5694 } else {
5695 item->setText(0, QString::fromUtf8(tree_nodes[node].text, len));
5696 }
5697 }
copyActionHandler()5698 void sexp_tree::copyActionHandler() {
5699 // If a clipboard already exist, unmark it as persistent and free old clipboard
5700 if (Sexp_clipboard != -1) {
5701 sexp_unmark_persistent(Sexp_clipboard);
5702 free_sexp2(Sexp_clipboard);
5703 }
5704
5705 // Allocate new clipboard and mark persistent
5706 Sexp_clipboard = save_branch(item_index, 1);
5707 sexp_mark_persistent(Sexp_clipboard);
5708 }
pasteActionHandler()5709 void sexp_tree::pasteActionHandler() {
5710 // the following assumptions are made..
5711 Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
5712 Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
5713
5714 if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
5715 expand_operator(item_index);
5716 replace_operator(CTEXT(Sexp_clipboard));
5717 if (Sexp_nodes[Sexp_clipboard].rest != -1) {
5718 load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index);
5719 auto i = tree_nodes[item_index].child;
5720 while (i != -1) {
5721 add_sub_tree(i, tree_nodes[item_index].handle);
5722 i = tree_nodes[i].next;
5723 }
5724 }
5725
5726 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
5727 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
5728 if (Sexp_nodes[Sexp_clipboard].type & SEXP_FLAG_VARIABLE) {
5729 int var_idx = get_index_sexp_variable_name(Sexp_nodes[Sexp_clipboard].text);
5730 Assert(var_idx > -1);
5731 replace_variable_data(var_idx, (SEXPT_VARIABLE | SEXPT_NUMBER | SEXPT_VALID));
5732 } else {
5733 expand_operator(item_index);
5734 replace_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID));
5735 }
5736
5737 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
5738 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
5739 if (Sexp_nodes[Sexp_clipboard].type & SEXP_FLAG_VARIABLE) {
5740 int var_idx = get_index_sexp_variable_name(Sexp_nodes[Sexp_clipboard].text);
5741 Assert(var_idx > -1);
5742 replace_variable_data(var_idx, (SEXPT_VARIABLE | SEXPT_STRING | SEXPT_VALID));
5743 } else {
5744 expand_operator(item_index);
5745 replace_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
5746 }
5747
5748 } else
5749 Assert(0); // unknown and/or invalid sexp type
5750
5751 expand_branch(currentItem());
5752
5753 }
insertOperatorAction(int op)5754 void sexp_tree::insertOperatorAction(int op) {
5755 int flags;
5756
5757 auto z = tree_nodes[item_index].parent;
5758 flags = tree_nodes[item_index].flags;
5759 auto node = allocate_node(z, item_index);
5760 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text.c_str());
5761 tree_nodes[node].flags = flags;
5762 QTreeWidgetItem* h;
5763 if (z >= 0) {
5764 h = tree_nodes[z].handle;
5765 } else {
5766 h = tree_nodes[item_index].handle->parent();
5767 if (!_interface->getFlags()[TreeFlags::LabeledRoot]) {
5768 h = nullptr;
5769 root_item = node;
5770 } else {
5771 rootNodeFormulaChanged(item_index, node);
5772 h->setData(0, FormulaDataRole, node);
5773 }
5774 }
5775
5776 auto item_handle = tree_nodes[node].handle =
5777 insert(Operators[op].text.c_str(), NodeImage::OPERATOR, h, tree_nodes[item_index].handle);
5778 move_branch(item_index, node);
5779
5780 setCurrentItemIndex(node);
5781 for (auto i = 1; i < Operators[op].min; i++) {
5782 add_default_operator(op, i);
5783 }
5784
5785 item_handle->setExpanded(true);
5786 modified();
5787 }
addNumberDataHandler()5788 void sexp_tree::addNumberDataHandler() {
5789 int theNode;
5790
5791 theNode = add_data("number", (SEXPT_NUMBER | SEXPT_VALID));
5792 beginItemEdit(tree_nodes[theNode].handle);
5793 }
addStringDataHandler()5794 void sexp_tree::addStringDataHandler() {
5795 int theNode;
5796
5797 theNode = add_data("string", (SEXPT_STRING | SEXPT_VALID));
5798 beginItemEdit(tree_nodes[theNode].handle);
5799 }
beginItemEdit(QTreeWidgetItem * item)5800 void sexp_tree::beginItemEdit(QTreeWidgetItem* item) {
5801 _currently_editing = true;
5802 editItem(item);
5803 }
addReplaceTypedDataHandler(int data_idx,bool replace)5804 void sexp_tree::addReplaceTypedDataHandler(int data_idx, bool replace) {
5805 Assert(item_index >= 0);
5806 int op;
5807 if (replace) {
5808 op = get_operator_index(tree_nodes[tree_nodes[item_index].parent].text);
5809 } else {
5810 op = get_operator_index(tree_nodes[item_index].text);
5811 }
5812 Assert(op >= 0);
5813
5814 auto argcount = replace ? Replace_count : Add_count;
5815
5816 auto type = query_operator_argument_type(op, argcount);
5817 auto list = get_listing_opf(type, item_index, argcount);
5818 Assert(list);
5819
5820 auto ptr = list;
5821 while (data_idx) {
5822 data_idx--;
5823 ptr = ptr->next;
5824 Assert(ptr);
5825 }
5826
5827 Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0));
5828 expand_operator(item_index);
5829 if (replace) {
5830 replace_data(ptr->text.c_str(), ptr->type);
5831 } else {
5832 add_data(ptr->text.c_str(), ptr->type);
5833 }
5834 list->destroy();
5835 }
addPasteActionHandler()5836 void sexp_tree::addPasteActionHandler() {
5837 // add paste, instead of replace.
5838 // the following assumptions are made..
5839 Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
5840 Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
5841
5842 if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
5843 expand_operator(item_index);
5844 add_operator(CTEXT(Sexp_clipboard));
5845 if (Sexp_nodes[Sexp_clipboard].rest != -1) {
5846 load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index);
5847 auto i = tree_nodes[item_index].child;
5848 while (i != -1) {
5849 add_sub_tree(i, tree_nodes[item_index].handle);
5850 i = tree_nodes[i].next;
5851 }
5852 }
5853
5854 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
5855 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
5856 expand_operator(item_index);
5857 add_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID));
5858
5859 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
5860 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
5861 expand_operator(item_index);
5862 add_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
5863
5864 } else
5865 Assert(0); // unknown and/or invalid sexp type
5866
5867 expand_branch(currentItem());
5868 }
setCurrentItemIndex(int node)5869 void sexp_tree::setCurrentItemIndex(int node) {
5870 item_index = node;
5871 if (node < 0) {
5872 setCurrentItem(nullptr);
5873 } else {
5874 setCurrentItem(tree_nodes[node].handle);
5875 }
5876 }
handleReplaceVariableAction(int id)5877 void sexp_tree::handleReplaceVariableAction(int id) {
5878 Assert(item_index >= 0);
5879
5880 // get index into list of type valid variables
5881 Assert( (id >= 0) && (id < MAX_SEXP_VARIABLES) );
5882
5883 int type = get_type(currentItem());
5884 Assert( (type & SEXPT_NUMBER) || (type & SEXPT_STRING) );
5885
5886 // don't do type check for modify-variable
5887 if (Modify_variable) {
5888 if (Sexp_variables[id].type & SEXP_VARIABLE_NUMBER) {
5889 type = SEXPT_NUMBER;
5890 } else if (Sexp_variables[id].type & SEXP_VARIABLE_STRING) {
5891 type = SEXPT_STRING;
5892 } else {
5893 Int3(); // unknown type
5894 }
5895
5896 } else {
5897 // verify type in tree is same as type in Sexp_variables array
5898 if (type & SEXPT_NUMBER) {
5899 Assert(Sexp_variables[id].type & SEXP_VARIABLE_NUMBER);
5900 }
5901
5902 if (type & SEXPT_STRING) {
5903 Assert( (Sexp_variables[id].type & SEXP_VARIABLE_STRING) );
5904 }
5905 }
5906
5907 // Replace data
5908 replace_variable_data(id, (type | SEXPT_VARIABLE));
5909
5910 }
handleNewItemSelected()5911 void sexp_tree::handleNewItemSelected() {
5912 auto selectedItem = currentItem();
5913
5914 update_help(selectedItem);
5915
5916 if (selectedItem == nullptr) {
5917 selectedRootChanged(-1);
5918 setCurrentItemIndex(-1);
5919 return;
5920 }
5921
5922 // Set the item index so that it is always up to date
5923 item_index = get_node(selectedItem);
5924
5925 auto item = selectedItem;
5926 while (item->parent() != nullptr) {
5927 item = item->parent();
5928 }
5929
5930 selectedRootChanged(item->data(0, FormulaDataRole).toInt());
5931 }
deleteCurrentItem()5932 void sexp_tree::deleteCurrentItem() {
5933 deleteActionHandler();
5934 }
getCurrentItemIndex() const5935 int sexp_tree::getCurrentItemIndex() const {
5936 return item_index;
5937 }
5938
5939 }
5940 }
5941