Refactored achievement conditions (#3831)

They now use the normal script engine and not a duplicated portion of code of it.
This is required for another pending update.

This also fixes atcommands not triggering status achievements.

Thanks to @aleos89 and @RadianFord
This commit is contained in:
Lemongrass3110 2019-01-13 17:26:25 +01:00 committed by GitHub
parent 444d7394c2
commit 4706115d5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 105 additions and 368 deletions

View File

@ -1690,72 +1690,72 @@ Achievements:
- ID: 200017
Group: "AG_GOAL_STATUS"
Name: "Bearish Power!"
Condition: " bStr >= 90 "
Condition: " readparam(bStr) >= 90 "
Score: 10
- ID: 200018
Group: "AG_GOAL_STATUS"
Name: "Overflowing Magic!"
Condition: " bInt >= 90 "
Condition: " readparam(bInt) >= 90 "
Score: 10
- ID: 200019
Group: "AG_GOAL_STATUS"
Name: "Healthy Body and Mental Health!"
Condition: " bVit >= 90 "
Condition: " readparam(bVit) >= 90 "
Score: 10
- ID: 200020
Group: "AG_GOAL_STATUS"
Name: "Speed of Light"
Condition: " bAgi >= 90 "
Condition: " readparam(bAgi) >= 90 "
Score: 10
- ID: 200021
Group: "AG_GOAL_STATUS"
Name: "Hawk Eyes"
Condition: " bDex >= 90 "
Condition: " readparam(bDex) >= 90 "
Score: 10
- ID: 200022
Group: "AG_GOAL_STATUS"
Name: "Maximum Luck"
Condition: " bLuk >= 90 "
Condition: " readparam(bLuk) >= 90 "
Score: 10
- ID: 200023
Group: "AG_GOAL_STATUS"
Name: "Dragonlike Power!"
Condition: " bStr >= 125 "
Condition: " readparam(bStr) >= 125 "
Reward:
Script: " sc_start SC_GIANTGROWTH,180000,1; "
Score: 20
- ID: 200024
Group: "AG_GOAL_STATUS"
Name: "Magic Insanity"
Condition: " bInt >= 125 "
Condition: " readparam(bInt) >= 125 "
Reward:
Script: " specialeffect2 EF_HASTEUP; bonus_script \"{ bonus2 bHPLossRate,100,10000; bonus bBaseAtk,20; bonus bAspdRate,25; }\",60,0,0,EFST_STEAMPACK; "
Score: 20
- ID: 200025
Group: "AG_GOAL_STATUS"
Name: "Rock Alloy"
Condition: " bVit >= 125 "
Condition: " readparam(bVit) >= 125 "
Reward:
Script: " specialeffect2 EF_HEAL3; sc_start2 SC_S_LIFEPOTION,600000,-5,5; "
Score: 20
- ID: 200026
Group: "AG_GOAL_STATUS"
Name: "Speed of Light"
Condition: " bAgi >= 125 "
Condition: " readparam(bAgi) >= 125 "
Reward:
Script: " specialeffect2 EF_STEAL; sc_start SC_INCFLEE2,60000,20; "
Score: 20
- ID: 200027
Group: "AG_GOAL_STATUS"
Name: "Falcon's Eyes"
Condition: " bDex >= 125 "
Condition: " readparam(bDex) >= 125 "
Reward:
Script: " specialeffect2 EF_MAGICALATTHIT; sc_start SC_INCCRI,300000,30; "
Score: 20
- ID: 200028
Group: "AG_GOAL_STATUS"
Name: "Lucky Fever"
Condition: " bLuk >= 125 "
Condition: " readparam(bLuk) >= 125 "
Reward:
Script: " specialeffect2 EF_GLORIA; sc_start SC_GLORIA,15000,0; "
Score: 20
@ -2000,12 +2000,10 @@ Achievements:
- ID: 220000
Group: "AG_CHATTING_CREATE"
Name: "Community begin"
Condition: " true "
Score: 10
- ID: 220001
Group: "AG_CHATTING_DYING"
Name: "A mouth only moment"
Condition: " true "
Score: 10
- ID: 220002
Group: "AG_CHATTING_COUNT"
@ -2025,12 +2023,10 @@ Achievements:
- ID: 220005
Group: "AG_PARTY"
Name: "Let's Party~"
Condition: " true "
Score: 10
- ID: 220006
Group: "AG_MARRY"
Name: "Married with who..?"
Condition: " true "
Reward:
TitleID: 1022
Score: 20
@ -2126,7 +2122,6 @@ Achievements:
- ID: 220022
Group: "AG_ENCHANT_FAIL"
Name: "Human's greed has no ending.."
Condition: " true "
Score: 10
- ID: 220023
Group: "AG_GET_ITEM"

View File

@ -1690,72 +1690,72 @@ Achievements:
- ID: 200017
Group: "AG_GOAL_STATUS"
Name: "Bearish Power!"
Condition: " bStr >= 90 "
Condition: " readparam(bStr) >= 90 "
Score: 10
- ID: 200018
Group: "AG_GOAL_STATUS"
Name: "Overflowing Magic!"
Condition: " bInt >= 90 "
Condition: " readparam(bInt) >= 90 "
Score: 10
- ID: 200019
Group: "AG_GOAL_STATUS"
Name: "Healthy Body and Mental Health!"
Condition: " bVit >= 90 "
Condition: " readparam(bVit) >= 90 "
Score: 10
- ID: 200020
Group: "AG_GOAL_STATUS"
Name: "Speed of Light"
Condition: " bAgi >= 90 "
Condition: " readparam(bAgi) >= 90 "
Score: 10
- ID: 200021
Group: "AG_GOAL_STATUS"
Name: "Hawk Eyes"
Condition: " bDex >= 90 "
Condition: " readparam(bDex) >= 90 "
Score: 10
- ID: 200022
Group: "AG_GOAL_STATUS"
Name: "Maximum Luck"
Condition: " bLuk >= 90 "
Condition: " readparam(bLuk) >= 90 "
Score: 10
- ID: 200023
Group: "AG_GOAL_STATUS"
Name: "Dragonlike Power!"
Condition: " bStr >= 125 "
Condition: " readparam(bStr) >= 125 "
Reward:
Script: " sc_start SC_GIANTGROWTH,180000,1; "
Score: 20
- ID: 200024
Group: "AG_GOAL_STATUS"
Name: "Magic Insanity"
Condition: " bInt >= 125 "
Condition: " readparam(bInt) >= 125 "
Reward:
Script: " specialeffect2 EF_HASTEUP; bonus_script \"{ bonus2 bHPLossRate,100,10000; bonus bBaseAtk,20; bonus bAspdRate,25; }\",60,0,0,EFST_STEAMPACK; "
Score: 20
- ID: 200025
Group: "AG_GOAL_STATUS"
Name: "Rock Alloy"
Condition: " bVit >= 125 "
Condition: " readparam(bVit) >= 125 "
Reward:
Script: " specialeffect2 EF_HEAL3; sc_start2 SC_S_LIFEPOTION,600000,-5,5; "
Score: 20
- ID: 200026
Group: "AG_GOAL_STATUS"
Name: "Speed of Light"
Condition: " bAgi >= 125 "
Condition: " readparam(bAgi) >= 125 "
Reward:
Script: " specialeffect2 EF_STEAL; sc_start SC_INCFLEE2,60000,20; "
Score: 20
- ID: 200027
Group: "AG_GOAL_STATUS"
Name: "Falcon's Eyes"
Condition: " bDex >= 125 "
Condition: " readparam(bDex) >= 125 "
Reward:
Script: " specialeffect2 EF_MAGICALATTHIT; sc_start SC_INCCRI,300000,30; "
Score: 20
- ID: 200028
Group: "AG_GOAL_STATUS"
Name: "Lucky Fever"
Condition: " bLuk >= 125 "
Condition: " readparam(bLuk) >= 125 "
Reward:
Script: " specialeffect2 EF_GLORIA; sc_start SC_GLORIA,15000,0; "
Score: 20
@ -2000,12 +2000,10 @@ Achievements:
- ID: 220000
Group: "AG_CHATTING_CREATE"
Name: "Community begin"
Condition: " true "
Score: 10
- ID: 220001
Group: "AG_CHATTING_DYING"
Name: "A mouth only moment"
Condition: " true "
Score: 10
- ID: 220002
Group: "AG_CHATTING_COUNT"
@ -2025,12 +2023,10 @@ Achievements:
- ID: 220005
Group: "AG_PARTY"
Name: "Let's Party~"
Condition: " true "
Score: 10
- ID: 220006
Group: "AG_MARRY"
Name: "Married with who..?"
Condition: " true "
Reward:
TitleID: 1022
Score: 20
@ -2126,7 +2122,6 @@ Achievements:
- ID: 220022
Group: "AG_ENCHANT_FAIL"
Name: "Human's greed has no ending.."
Condition: " true "
Score: 10
- ID: 220023
Group: "AG_GET_ITEM"

View File

@ -28,11 +28,6 @@
#include "script.hpp"
#include "status.hpp"
static jmp_buf av_error_jump;
static char* av_error_msg;
static const char* av_error_pos;
static int av_error_report;
std::unordered_map<int, std::shared_ptr<s_achievement_db>> achievements;
std::vector<int> achievement_mobs; // Avoids checking achievements on every mob killed
@ -502,6 +497,40 @@ int *achievement_level(struct map_session_data *sd, bool flag)
return info;
}
static bool achievement_check_condition( struct script_code* condition, struct map_session_data* sd, const int* count ){
// Save the old script the player was attached to
struct script_state* previous_st = sd->st;
// Only if there was an old script
if( previous_st != nullptr ){
// Detach the player from the current script
script_detach_rid(previous_st);
}
run_script( condition, 0, sd->bl.id, fake_nd->bl.id );
struct script_state* st = sd->st;
int value = 0;
if( st != nullptr ){
value = script_getnum( st, 2 );
script_free_state(st);
}
// If an old script is present
if( previous_st != nullptr ){
// Because of detach the RID will be removed, so we need to restore it
previous_st->rid = sd->bl.id;
// Reattach the player to it, so that the limitations of that script kick back in
script_attach_state( previous_st );
}
return value != 0;
}
/**
* Update achievement objectives.
* @param sd: Player to update
@ -657,8 +686,13 @@ void achievement_update_objective(struct map_session_data *sd, enum e_achievemen
count.fill(0); // Clear out array before setting values
va_start(ap, arg_count);
for (i = 0; i < arg_count; i++)
for (i = 0; i < arg_count; i++){
std::string name = "ARG" + std::to_string(i);
count[i] = va_arg(ap, int);
pc_setglobalreg( sd, add_str( name.c_str() ), (int)count[i] );
}
va_end(ap);
switch(group) {
@ -671,309 +705,14 @@ void achievement_update_objective(struct map_session_data *sd, enum e_achievemen
achievement_update_objectives(sd, ach.second, group, count);
break;
}
}
}
/*==========================================
* Achievement condition parsing section
*------------------------------------------*/
static void disp_error_message2(const char *mes,const char *pos,int report)
{
av_error_msg = aStrdup(mes);
av_error_pos = pos;
av_error_report = report;
longjmp(av_error_jump, 1);
}
#define disp_error_message(mes,pos) disp_error_message2(mes,pos,1)
// Remove variables that might have been set
for (i = 0; i < arg_count; i++){
std::string name = "ARG" + std::to_string(i);
/**
* Checks the condition of an achievement.
* @param condition: Achievement condition
* @param sd: Player data
* @param count: Script arguments
* @return The result of the condition.
*/
long long achievement_check_condition(std::shared_ptr<struct av_condition> condition, struct map_session_data *sd, const int *count)
{
long long left = 0;
long long right = 0;
// Reduce the recursion, almost all calls will be C_PARAM, C_NAME or C_ARG
if (condition->left) {
if (condition->left->op == C_NAME || condition->left->op == C_INT)
left = condition->left->value;
else if (condition->left->op == C_PARAM)
left = pc_readparam(sd, (int)condition->left->value);
else if (condition->left->op == C_ARG && condition->left->value < MAX_ACHIEVEMENT_OBJECTIVES)
left = count[condition->left->value];
else
left = achievement_check_condition(condition->left, sd, count);
}
if (condition->right) {
if (condition->right->op == C_NAME || condition->right->op == C_INT)
right = condition->right->value;
else if (condition->right->op == C_PARAM)
right = pc_readparam(sd, (int)condition->right->value);
else if (condition->right->op == C_ARG && condition->right->value < MAX_ACHIEVEMENT_OBJECTIVES)
right = count[condition->right->value];
else
right = achievement_check_condition(condition->right, sd, count);
}
switch(condition->op) {
case C_NOP:
return false;
case C_NAME:
case C_INT:
return condition->value;
case C_PARAM:
return pc_readparam(sd, (int)condition->value);
case C_LOR:
return left || right;
case C_LAND:
return left && right;
case C_LE:
return left <= right;
case C_LT:
return left < right;
case C_GE:
return left >= right;
case C_GT:
return left > right;
case C_EQ:
return left == right;
case C_NE:
return left != right;
case C_XOR:
return left ^ right;
case C_OR:
return left || right;
case C_AND:
return left & right;
case C_ADD:
return left + right;
case C_SUB:
return left - right;
case C_MUL:
return left * right;
case C_DIV:
return left / right;
case C_MOD:
return left % right;
case C_NEG:
return -left;
case C_LNOT:
return !left;
case C_NOT:
return ~left;
case C_R_SHIFT:
return left >> right;
case C_L_SHIFT:
return left << right;
case C_ARG:
if (condition->value < MAX_ACHIEVEMENT_OBJECTIVES)
return count[condition->value];
return false;
default:
ShowError("achievement_check_condition: unexpected operator: %d\n", condition->op);
return false;
}
return false;
}
/**
* Skips a word. A word consists of undercores and/or alphanumeric characters, and valid variable prefixes/postfixes.
* @param p: Word
* @return Next word
*/
static const char *skip_word(const char *p)
{
while (ISALNUM(*p) || *p == '_')
++p;
if (*p == '$') // String
p++;
return p;
}
/**
* Analyze an achievement's condition script
* @param p: Word
* @param parent: Parent node
* @return Word
*/
const char *av_parse_simpleexpr(const char *p, std::shared_ptr<struct av_condition> parent)
{
long long i;
p = skip_space(p);
if(*p == ';' || *p == ',')
disp_error_message("av_parse_simpleexpr: unexpected character.", p);
if(*p == '(') {
p = av_parse_subexpr(p + 1, -1, parent);
p = skip_space(p);
if (*p != ')')
disp_error_message("av_parse_simpleexpr: unmatched ')'", p);
++p;
} else if(is_number(p)) {
char *np;
while(*p == '0' && ISDIGIT(p[1]))
p++;
i = strtoll(p, &np, 0);
if (i < INT_MIN) {
i = INT_MIN;
disp_error_message("av_parse_simpleexpr: underflow detected, capping value to INT_MIN.", p);
} else if (i > INT_MAX) {
i = INT_MAX;
disp_error_message("av_parse_simpleexpr: underflow detected, capping value to INT_MAX.", p);
pc_setglobalreg( sd, add_str( name.c_str() ), 0 );
}
parent->op = C_INT;
parent->value = i;
p = np;
} else {
int v, len;
if (skip_word(p) == p)
disp_error_message("av_parse_simpleexpr: unexpected character.", p);
len = skip_word(p) - p;
if (len == 0)
disp_error_message("av_parse_simpleexpr: invalid word. A word consists of undercores and/or alphanumeric characters.", p);
std::unique_ptr<char[]> word(new char[len + 1]);
memcpy(word.get(), p, len);
word[len] = 0;
if (script_get_parameter((const char*)&word[0], &v))
parent->op = C_PARAM;
else if (script_get_constant(&word[0], &v)) {
if (word[0] == 'b' && ISUPPER(word[1])) // Consider b* variables as parameters (because they... are?)
parent->op = C_PARAM;
else
parent->op = C_NAME;
} else {
if (word[0] == 'A' && word[1] == 'R' && word[2] == 'G' && ISDIGIT(word[3])) { // Special constants used to set temporary variables
parent->op = C_ARG;
v = atoi(&word[0] + 3);
} else {
disp_error_message("av_parse_simpleexpr: invalid constant.", p);
}
}
parent->value = v;
p = skip_word(p);
}
return p;
}
/**
* Analysis of an achievement's expression
* @param p: Word
* @param parent: Parent node
* @return Word
*/
const char *av_parse_subexpr(const char* p, int limit, std::shared_ptr<struct av_condition> parent)
{
int op, opl, len;
p = skip_space(p);
parent->left.reset(new av_condition());
if ((op = C_NEG, *p == '-') || (op = C_LNOT, *p == '!') || (op = C_NOT, *p == '~')) { // Unary - ! ~ operators
p = av_parse_subexpr(p + 1, 11, parent->left);
parent->op = op;
} else
p = av_parse_simpleexpr(p, parent->left);
p = skip_space(p);
while((
((op=C_ADD,opl=9,len=1,*p=='+') && p[1]!='+') ||
((op=C_SUB,opl=9,len=1,*p=='-') && p[1]!='-') ||
(op=C_MUL,opl=10,len=1,*p=='*') ||
(op=C_DIV,opl=10,len=1,*p=='/') ||
(op=C_MOD,opl=10,len=1,*p=='%') ||
(op=C_LAND,opl=2,len=2,*p=='&' && p[1]=='&') ||
(op=C_AND,opl=5,len=1,*p=='&') ||
(op=C_LOR,opl=1,len=2,*p=='|' && p[1]=='|') ||
(op=C_OR,opl=3,len=1,*p=='|') ||
(op=C_XOR,opl=4,len=1,*p=='^') ||
(op=C_EQ,opl=6,len=2,*p=='=' && p[1]=='=') ||
(op=C_NE,opl=6,len=2,*p=='!' && p[1]=='=') ||
(op=C_R_SHIFT,opl=8,len=2,*p=='>' && p[1]=='>') ||
(op=C_GE,opl=7,len=2,*p=='>' && p[1]=='=') ||
(op=C_GT,opl=7,len=1,*p=='>') ||
(op=C_L_SHIFT,opl=8,len=2,*p=='<' && p[1]=='<') ||
(op=C_LE,opl=7,len=2,*p=='<' && p[1]=='=') ||
(op=C_LT,opl=7,len=1,*p=='<')) && opl>limit) {
p += len;
if (parent->right) { // Chain conditions
std::shared_ptr<struct av_condition> condition(new struct av_condition());
condition->op = parent->op;
condition->left = parent->left;
condition->right = parent->right;
parent->left = condition;
parent->right.reset();
}
parent->right.reset(new av_condition());
p = av_parse_subexpr(p, opl, parent->right);
parent->op = op;
p = skip_space(p);
}
if (parent->op == C_NOP && !parent->right) { // Move the node up
parent->right = parent->left->right;
parent->op = parent->left->op;
parent->value = parent->left->value;
parent->left = parent->left->left;
}
return p;
}
/**
* Parses a condition from a script.
* @param p: The script buffer.
* @param file: The file being parsed.
* @param line: The current achievement line number.
* @return The parsed achievement condition.
*/
std::shared_ptr<struct av_condition> parse_condition(const char *p, const char *file, int line)
{
std::shared_ptr<struct av_condition> condition;
if (setjmp(av_error_jump) != 0) {
if (av_error_report)
script_error(p,file,line,av_error_msg,av_error_pos);
aFree(av_error_msg);
condition.reset();
return NULL;
}
switch(*p) {
case ')': case ';': case ':': case '[': case ']': case '}':
disp_error_message("parse_condition: unexpected character.", p);
}
condition.reset(new av_condition());
av_parse_subexpr(p, -1, condition);
return condition;
}
static void yaml_invalid_warning(const char* fmt, const YAML::Node &node, const std::string &file) {
@ -1090,9 +829,14 @@ bool achievement_read_db_sub(const YAML::Node &node, int n, const std::string &s
yaml_invalid_warning("achievement_read_db_sub: Achievement definition with invalid condition field in '" CL_WHITE "%s" CL_RESET "', skipping.\n", node, source);
return false;
}
entry->condition = parse_condition(condition.c_str(), source.c_str(), n);
}
if( condition.find( "achievement_condition" ) == std::string::npos ){
condition = "achievement_condition( " + condition + " );";
}
entry->condition = parse_script( condition.c_str(), source.c_str(), node["Condition"].Mark().line, SCRIPT_IGNORE_EXTERNAL_BRACKETS );
}
if (node["Map"]) {
try {
mapname = node["Map"].as<std::string>();
@ -1204,16 +948,6 @@ void achievement_read_db(void)
return;
}
/**
* Recursive method to free an achievement condition (probably not needed anymore, but just in case)
* @param condition: Condition to clear
*/
void achievement_script_free(std::shared_ptr<struct av_condition> condition)
{
condition->left.reset();
condition->right.reset();
}
/**
* Reloads the achievement database
*/
@ -1260,6 +994,15 @@ s_achievement_db::s_achievement_db()
, has_dependent(0)
{}
/**
* Achievement deconstructor
*/
s_achievement_db::~s_achievement_db()
{
if (condition)
script_free_code(condition);
}
/**
* Achievement reward constructor
*/

View File

@ -68,22 +68,13 @@ struct achievement_target {
int count;
};
struct av_condition {
int op;
std::shared_ptr<struct av_condition> left;
std::shared_ptr<struct av_condition> right;
long long value;
av_condition() : op(0), left(nullptr), right(nullptr), value(0) {}
};
struct s_achievement_db {
int achievement_id;
std::string name;
enum e_achievement_group group;
std::vector <achievement_target> targets;
std::vector <int> dependent_ids;
std::shared_ptr<struct av_condition> condition;
struct script_code* condition;
int16 mapindex;
struct ach_reward {
unsigned short nameid, amount;
@ -96,6 +87,7 @@ struct s_achievement_db {
int has_dependent; // Used for quick updating of achievements that depend on others - this is their ID
s_achievement_db();
~s_achievement_db();
};
bool achievement_exists(int achievement_id);
@ -117,10 +109,4 @@ void achievement_db_reload(void);
void do_init_achievement(void);
void do_final_achievement(void);
// Parser
const char *av_parse_subexpr(const char *p,int limit, std::shared_ptr<struct av_condition> parent);
const char *av_parse_simpleexpr(const char *p, std::shared_ptr<struct av_condition> parent);
long long achievement_check_condition(std::shared_ptr<struct av_condition> condition, struct map_session_data *sd, const int *count);
void achievement_script_free(std::shared_ptr<struct av_condition> condition);
#endif /* ACHIEVEMENT_HPP */

View File

@ -2610,6 +2610,8 @@ ACMD_FUNC(param)
clif_updatestatus(sd, SP_USTR + i);
status_calc_pc(sd, SCO_FORCE);
clif_displaymessage(fd, msg_txt(sd,42)); // Stat changed.
achievement_update_objective(sd, AG_GOAL_STATUS, 0);
} else {
if (value < 0)
clif_displaymessage(fd, msg_txt(sd,41)); // Unable to decrease the number/value.
@ -2681,6 +2683,8 @@ ACMD_FUNC(stat_all)
if (count > 0) { // if at least 1 stat modified
status_calc_pc(sd, SCO_FORCE);
clif_displaymessage(fd, msg_txt(sd,84)); // All stats changed!
achievement_update_objective(sd, AG_GOAL_STATUS, 0);
} else {
if (value < 0)
clif_displaymessage(fd, msg_txt(sd,177)); // You cannot decrease that stat anymore.

View File

@ -12197,7 +12197,7 @@ BUILDIN_FUNC(warpwaitingpc)
/// Detaches a character from a script.
///
/// @param st Script state to detach the character from.
static void script_detach_rid(struct script_state* st)
void script_detach_rid(struct script_state* st)
{
if(st->rid)
{
@ -24061,6 +24061,17 @@ BUILDIN_FUNC( camerainfo ){
#endif
}
// This function is only meant to be used inside of achievement conditions
BUILDIN_FUNC(achievement_condition){
// Push what we get from the script
script_pushint( st, 2 );
// Otherwise the script is freed afterwards
st->state = RERUNLINE;
return SCRIPT_CMD_SUCCESS;
}
#include "../custom/script.inc"
// declarations that were supposed to be exported from npc_chat.c
@ -24724,6 +24735,8 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(is_guild_leader,"?"),
BUILDIN_DEF(is_party_leader,"?"),
BUILDIN_DEF(camerainfo,"iii?"),
BUILDIN_DEF(achievement_condition,"i"),
#include "../custom/script_def.inc"
{NULL,NULL,NULL},

View File

@ -1935,6 +1935,7 @@ TIMER_FUNC(run_script_timer);
void script_stop_sleeptimers(int id);
struct linkdb_node *script_erase_sleepdb(struct linkdb_node *n);
void script_attach_state(struct script_state* st);
void script_detach_rid(struct script_state* st);
void run_script_main(struct script_state *st);
void script_stop_scriptinstances(struct script_code *code);