Hard Light Productions Forums
Modding, Mission Design, and Coding => The Scripting Workshop => Topic started by: niffiwan on March 30, 2014, 12:47:31 am
-
As many of you know, script-eval is limited to being able to call
31 32 characters worth of script. This leads to *fun* workarounds like really short script names and using variables (also with short names) to hold script parameters. Then Axem said to me on IRC the other day, "What if all the script-eval arguments could be concatenated together and passed to the scripting system?" Intriguing concept... and this is the result :)
commit c7363f011de4260572fcfe3bf9d25c5961be6579
Author: niffiwan <[email protected]>
Date: Sun Mar 30 15:04:17 2014 +1000
allow script-eval to run scripts longer than 31 chars
It's a workaround to the sexp TOKEN_LENGTH limit
The script can be broken up across multiple arguments
e.g.
( script-eval
"ba.warning('foo"
" more and more and more"
" and the rest')"
)
Kudos to Axem for suggesting the idea
diff --git a/code/parse/scripting.cpp b/code/parse/scripting.cpp
index 1c3060f..6d04a80 100644
--- a/code/parse/scripting.cpp
+++ b/code/parse/scripting.cpp
@@ -1068,7 +1068,7 @@ int script_state::OutputMeta(char *filename)
return 1;
}
-bool script_state::EvalString(char* string, char *format, void *rtn, char *debug_str)
+bool script_state::EvalString(const char* string, char *format, void *rtn, const char *debug_str)
{
char lastchar = string[strlen(string)-1];
diff --git a/code/parse/scripting.h b/code/parse/scripting.h
index 9d28528..f7f8056 100644
--- a/code/parse/scripting.h
+++ b/code/parse/scripting.h
@@ -189,7 +189,7 @@ public:
void RemHookVars(unsigned int num, ...);
//***Hook creation functions
- bool EvalString(char* string, char *format=NULL, void *rtn=NULL, char *debug_str=NULL);
+ bool EvalString(const char* string, char *format=NULL, void *rtn=NULL, const char *debug_str=NULL);
void ParseChunk(script_hook *dest, char* debug_str=NULL);
bool ParseCondition(const char *filename="<Unknown>");
diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp
index 18d0d71..50024d7 100644
--- a/code/parse/sexp.cpp
+++ b/code/parse/sexp.cpp
@@ -21391,6 +21391,7 @@ int sexp_script_eval(int node, int return_type)
{
int n = node;
char *s = CTEXT(n);
+ SCP_string lua_cmd;
bool success = false;
int r = -1;
@@ -21406,9 +21407,10 @@ int sexp_script_eval(int node, int return_type)
case OPR_NULL:
while(n != -1)
{
- success = Script_system.EvalString(s, NULL, NULL, CTEXT(n));
+ lua_cmd.append(CTEXT(n));
n = CDR(n);
}
+ success = Script_system.EvalString(lua_cmd.c_str(), NULL, NULL, lua_cmd.c_str());
break;
default:
Error(LOCATION, "Bad type passed to sexp_script_eval - get a coder");
@@ -32560,9 +32562,11 @@ sexp_help_struct Sexp_help[] = {
},
{OP_SCRIPT_EVAL, "script-eval\r\n"
- "\tEvaluates script"
+ "\tEvaluates one script\r\n"
+ "\tAdditional arguments are the rest of the script in 31 (+NULL character) chunks\r\n\r\n"
"Takes at least 1 argument...\r\n"
"\t1:\tScript to evaluate\r\n"
+ "\tRest:\tRemainder of script if the script length exceeds 32 characters\r\n"
},
{OP_FORCE_GLIDE, "force-glide\r\n"
In other words, at the cost of a little ugliness in the script-eval arguments, you can now pass an arbitrary1 length script to script-eval.
Before I consider committing this to trunk, I'd like some wider testing of the solution, so please grab the executables linked here and let me know the results.
http://www.mediafire.com/download/e9u1z5bkpv9o0wc/new-script-eval.7z (http://www.mediafire.com/download/e9u1z5bkpv9o0wc/new-script-eval.7z)
Lastly, please note that script-eval was already designed to accept multiple parameters, but the implementation was broken. It called the 1st argument in place of all arguments. i.e. 3 arguments equalled three calls of the 1st argument. Therefore I'm confident that changing the sexp like this won't break any existing missions.
1. There are no prizes if you break it by passing 11MB of text to script-eval :p
edit: (yep, its 32 chars, not 31)
-
SORCERY
-
:D
Here's another example using a slightly modified turret hotkey script (http://www.hard-light.net/forums/index.php?topic=82287.0):
(yes, turretHotKeyAdder is a bit contrived in order to exceed the 32 char limit... the ship names for thkShip() on the other hand...)
$Formula: ( when
( true )
( script-eval
"thkEnable()"
)
( script-eval
"thkShip('GTC Aeolus 1'," " 'GTC 2')"
)
( script-eval "turretHotKeyAdder(1," " 'Turret01')")
( script-eval "turretHotKeyAdder(1," " 'Turret02')")
( script-eval "turretHotKeyAdder(1," " 'Turret03')")
( script-eval "turretHotKeyAdder(1," " 'Turret04')")
( script-eval "turretHotKeyAdder(2," " 'Turret01')")
( script-eval "turretHotKeyAdder(2," " 'Turret02')")
( script-eval "turretHotKeyAdder(2," " 'Turret03')")
( script-eval "turretHotKeyAdder(2," " 'Turret04')")
( script-eval "thkKey('F12')")
)
+Name: turrethotkey
+Repeat Count: 1
+Interval: 1
-
I've been intending on fixing this for a long time but never got around to it, so this is great.
However, wouldn't it be better if instead of brute string concatenation, you could also directly pass arguments to a Lua function as the SEXP arguments?
For example...
( script-eval
"turretHotKeyAdder()"
"1"
"Turret01"
)
I guess it might make more sense to have a separate script-eval-func SEXP though.
-
A seperate SEXP would be better for calling a single Lua functions as the solution niffiwan has implemented can also be used for more complicated things than just executing a single function.
-
Yeah, a separate script-eval-func sexp would be ideal for that (and pretty cool).
I mean with this new way of doing things you can do something like...
"hv.Player.Target = mn.Ships['"
"@StringVar[GTC Orff]"
"']
to force a player's target to a ship name specified in a sexp variable!
EDIT: Haha, or even
"for i = 1, #mn.Ships do "
"mn.Ships[i].HitpointsLeft"
" = 50000 end"
To give every ship 50,000 HP regardless of their actual max health (hello 6500% hull integrity fighters!)
-
EDIT: Haha, or even
"for i = 1, #mn.Ships do "
"mn.Ships[i].HitpointsLeft"
" = 50000 end"
To give every ship 50,000 HP regardless of their actual max health (hello 6500% hull integrity fighters!)
Oh dear.. JAD 2.2245683 is going to be hell. :lol:
-
I apparently didn't look at this closely enough before... I'll need to do some more specific testing and checks, but I'm pretty confident multiple script-eval arguments work or my checkpoints in BtA wouldn't.
EDIT: After my tests, it's looking more like I was too good at defensibly FREDing and my checkpoint in the one mission affected worked for an entirely different reason. So I defer to your coding knowledge.
-
Has this been committed to trunk yet? I made some changes to the script eval functions and I don't want to introduce behavior that breaks later on.
-
I'm pretty sure it's not in trunk.
-
Ahahaha. It's nearly been a year since I 1st posted this :nervous:
Based on feedback I've decided to move this to a separate sexp, script-eval-block rather than potentially stomp all over existing uses of script-eval.
commit 50e588bb9e0b7fccd836058f922a500e13a112db
Author: niffiwan <[email protected]>
Date: Mon Mar 16 18:48:13 2015 +1000
add script-eval-block sexp
This is a workaround to the sexp TOKEN_LENGTH limit
The script can be split across multiple arguments to allow scripts
longer than 32 chars (i.e. current TOKEN_LENGTH)
e.g.
( script-eval
"ba.warning('foo"
" more and more and more"
" and the rest')"
)
Kudos to Axem for suggesting the idea
(And to zookeeper for reminding me to actually commit it!)
diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp
index f86d2d9..f5f6b1e 100644
--- a/code/parse/sexp.cpp
+++ b/code/parse/sexp.cpp
@@ -683,6 +683,7 @@ sexp_oper Operators[] = {
{ "damaged-escort-priority-all", OP_DAMAGED_ESCORT_LIST_ALL, 1, MAX_COMPLETE_ESCORT_LIST, SEXP_ACTION_OPERATOR, }, // Goober5000
{ "set-support-ship", OP_SET_SUPPORT_SHIP, 6, 7, SEXP_ACTION_OPERATOR, }, // Goober5000
{ "script-eval", OP_SCRIPT_EVAL, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
+ { "script-eval-block", OP_SCRIPT_EVAL_BLOCK, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
{ "multi-eval", OP_SCRIPT_EVAL_MULTI, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
{ "debug", OP_DEBUG, 2, 2, SEXP_ACTION_OPERATOR, }, // Karajorma
{ "do-nothing", OP_NOP, 0, 0, SEXP_ACTION_OPERATOR, },
@@ -21659,7 +21660,7 @@ void multi_sexp_show_hide_jumpnode(bool show)
//WMC - This is a bit of a hack, however, it's easier than
//coding in a whole new Script_system function.
-int sexp_script_eval(int node, int return_type)
+int sexp_script_eval(int node, int return_type, bool concat_args = false)
{
int n = node;
@@ -21711,16 +21712,33 @@ int sexp_script_eval(int node, int return_type)
break;
case OPR_NULL:
{
+ SCP_string script_cmd;
while (n != -1)
{
char* s = CTEXT(n);
- bool success = Script_system.EvalString(s, NULL, NULL);
- if (!success)
- Warning(LOCATION, "sexp-script-eval failed to evaluate string \"%s\"; check your syntax", s);
+ if (concat_args)
+ {
+ script_cmd.append(CTEXT(n));
+ }
+ else
+ {
+ bool success = Script_system.EvalString(s, NULL, NULL);
+
+ if (!success)
+ Warning(LOCATION, "sexp-script-eval failed to evaluate string \"%s\"; check your syntax", s);
+ }
n = CDR(n);
}
+
+ if (concat_args)
+ {
+ bool success = Script_system.EvalString(script_cmd.c_str(), NULL, NULL);
+
+ if (!success)
+ Warning(LOCATION, "sexp-script-eval failed to evaluate string \"%s\"; check your syntax", script_cmd.c_str());
+ }
}
break;
default:
@@ -24515,6 +24533,10 @@ int eval_sexp(int cur_node, int referenced_node)
sexp_val = sexp_script_eval(node, OPR_NULL);
break;
+ case OP_SCRIPT_EVAL_BLOCK:
+ sexp_val = sexp_script_eval(node, OPR_NULL, true);
+ break;
+
case OP_SCRIPT_EVAL_MULTI:
sexp_script_eval_multi(node);
sexp_val = SEXP_TRUE;
@@ -25552,6 +25574,7 @@ int query_operator_return_type(int op)
case OP_SET_SECONDARY_WEAPON:
case OP_SET_NUM_COUNTERMEASURES:
case OP_SCRIPT_EVAL:
+ case OP_SCRIPT_EVAL_BLOCK:
case OP_SCRIPT_EVAL_STRING:
case OP_SCRIPT_EVAL_MULTI:
case OP_ENABLE_BUILTIN_MESSAGES:
@@ -27634,6 +27657,7 @@ int query_operator_argument_type(int op, int argnum)
return OPF_BOOL;
case OP_SCRIPT_EVAL_NUM:
+ case OP_SCRIPT_EVAL_BLOCK:
case OP_SCRIPT_EVAL:
return OPF_STRING;
@@ -29420,6 +29444,7 @@ int get_subcategory(int sexp_id)
case OP_DAMAGED_ESCORT_LIST_ALL:
case OP_SET_SUPPORT_SHIP:
case OP_SCRIPT_EVAL_STRING:
+ case OP_SCRIPT_EVAL_BLOCK:
case OP_SCRIPT_EVAL:
case OP_SCRIPT_EVAL_MULTI:
return CHANGE_SUBCATEGORY_OTHER;
@@ -33056,6 +33081,12 @@ sexp_help_struct Sexp_help[] = {
"\t1:\tScript to evaluate\r\n"
},
+ {OP_SCRIPT_EVAL_BLOCK, "script-eval-block\r\n"
+ "\tEvaluates the concatenation of all arguments as a script\r\n"
+ "Takes at least 1 argument...\r\n"
+ "\tAll:\tScript to evaluate\r\n"
+ },
+
{OP_SCRIPT_EVAL_MULTI, "multi-eval\r\n"
"\tEvaluates script\r\n\r\n"
"Takes at least 2 arguments...\r\n"
diff --git a/code/parse/sexp.h b/code/parse/sexp.h
index f215c63..48fec5a 100644
--- a/code/parse/sexp.h
+++ b/code/parse/sexp.h
@@ -726,6 +726,7 @@ class waypoint_list;
#define OP_HUD_SET_RETAIL_GAUGE_ACTIVE (0x0027 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // The E, just revamped a bit by Axem
#define OP_SCRIPT_EVAL_MULTI (0x0028 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // Karajorma
#define OP_PAUSE_SOUND_FROM_FILE (0x0029 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // Goober5000
+#define OP_SCRIPT_EVAL_BLOCK (0x002a | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // niffiwan
// defined for AI goals
#define OP_AI_CHASE (0x0000 | OP_CATEGORY_AI | OP_NONCAMPAIGN_FLAG)
If you have further feedback let me know this week as I'm going to commit this (for realz this time) next weekend :D
/me runs off and sets a reminder in his calendar...
-
For the love of gods YES!!!