summaryrefslogtreecommitdiff
path: root/src/map/script-call.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/script-call.cpp')
-rw-r--r--src/map/script-call.cpp1042
1 files changed, 1042 insertions, 0 deletions
diff --git a/src/map/script-call.cpp b/src/map/script-call.cpp
new file mode 100644
index 0000000..f889e4b
--- /dev/null
+++ b/src/map/script-call.cpp
@@ -0,0 +1,1042 @@
+#include "script-call-internal.hpp"
+// script-call.cpp - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011 Chuck Miller
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2013 wushin
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "../generic/intern-pool.hpp"
+
+#include "../io/cxxstdio.hpp"
+#include "../io/cxxstdio_enums.hpp"
+
+#include "battle.hpp"
+#include "map.hpp"
+#include "npc.hpp"
+#include "pc.hpp"
+#include "script-fun.hpp"
+#include "script-parse-internal.hpp"
+#include "script-persist.hpp"
+#include "script-startup-internal.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+constexpr bool DEBUG_RUN = false;
+
+static
+struct ScriptConfigRun
+{
+ static const
+ int check_cmdcount = 8192;
+ static const
+ int check_gotocount = 512;
+} script_config;
+
+
+/*==========================================
+ * ridからsdへの解決
+ *------------------------------------------
+ */
+dumb_ptr<map_session_data> script_rid2sd(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = map_id2sd(st->rid);
+ if (!sd)
+ {
+ PRINTF("script_rid2sd: fatal error ! player not attached!\n"_fmt);
+ }
+ return sd;
+}
+
+/*==========================================
+ * 変数の読み取り
+ *------------------------------------------
+ */
+void get_val(dumb_ptr<map_session_data> sd, struct script_data *data)
+{
+ MATCH (*data)
+ {
+ CASE (const ScriptDataParam&, u)
+ {
+ if (sd == nullptr)
+ PRINTF("get_val error param SP::%d\n"_fmt, u.reg.sp());
+ int numi = 0;
+ if (sd)
+ numi = pc_readparam(sd, u.reg.sp());
+ *data = ScriptDataInt{numi};
+ }
+ CASE (const ScriptDataVariable&, u)
+ {
+ ZString name_ = variable_names.outtern(u.reg.base());
+ VarName name = stringish<VarName>(name_);
+ char prefix = name.front();
+ char postfix = name.back();
+
+ if (prefix != '$')
+ {
+ if (sd == nullptr)
+ PRINTF("get_val error name?:%s\n"_fmt, name);
+ }
+ if (postfix == '$')
+ {
+ RString str;
+ if (prefix == '@')
+ {
+ if (sd)
+ str = pc_readregstr(sd, u.reg);
+ }
+ else if (prefix == '$')
+ {
+ RString *s = mapregstr_db.search(u.reg);
+ if (s)
+ str = *s;
+ }
+ else
+ {
+ PRINTF("script: get_val: illegal scope string variable.\n"_fmt);
+ str = "!!ERROR!!"_s;
+ }
+ *data = ScriptDataStr{str};
+ }
+ else
+ {
+ int numi = 0;
+ if (prefix == '@')
+ {
+ if (sd)
+ numi = pc_readreg(sd, u.reg);
+ }
+ else if (prefix == '$')
+ {
+ numi = mapreg_db.get(u.reg);
+ }
+ else if (prefix == '#')
+ {
+ if (name[1] == '#')
+ {
+ if (sd)
+ numi = pc_readaccountreg2(sd, name);
+ }
+ else
+ {
+ if (sd)
+ numi = pc_readaccountreg(sd, name);
+ }
+ }
+ else
+ {
+ if (sd)
+ numi = pc_readglobalreg(sd, name);
+ }
+ *data = ScriptDataInt{numi};
+ }
+ }
+ }
+}
+
+void get_val(ScriptState *st, struct script_data *data)
+{
+ dumb_ptr<map_session_data> sd = st->rid ? map_id2sd(st->rid) : nullptr;
+ get_val(sd, data);
+}
+
+/*==========================================
+ * 変数の読み取り2
+ *------------------------------------------
+ */
+struct script_data get_val2(ScriptState *st, SIR reg)
+{
+ struct script_data dat = ScriptDataVariable{reg};
+ get_val(st, &dat);
+ return dat;
+}
+
+/*==========================================
+ * 変数設定用
+ *------------------------------------------
+ */
+void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, struct script_data vd)
+{
+ if (type == VariableCode::PARAM)
+ {
+ int val = vd.get_if<ScriptDataInt>()->numi;
+ pc_setparam(sd, reg.sp(), val);
+ return;
+ }
+ assert (type == VariableCode::VARIABLE);
+
+ ZString name_ = variable_names.outtern(reg.base());
+ VarName name = stringish<VarName>(name_);
+ char prefix = name.front();
+ char postfix = name.back();
+
+ if (postfix == '$')
+ {
+ RString str = vd.get_if<ScriptDataStr>()->str;
+ if (prefix == '@')
+ {
+ pc_setregstr(sd, reg, str);
+ }
+ else if (prefix == '$')
+ {
+ mapreg_setregstr(reg, str);
+ }
+ else
+ {
+ PRINTF("script: set_reg: illegal scope string variable !"_fmt);
+ }
+ }
+ else
+ {
+ int val = vd.get_if<ScriptDataInt>()->numi;
+ if (prefix == '@')
+ {
+ pc_setreg(sd, reg, val);
+ }
+ else if (prefix == '$')
+ {
+ mapreg_setreg(reg, val);
+ }
+ else if (prefix == '#')
+ {
+ if (name[1] == '#')
+ pc_setaccountreg2(sd, name, val);
+ else
+ pc_setaccountreg(sd, name, val);
+ }
+ else
+ {
+ pc_setglobalreg(sd, name, val);
+ }
+ }
+}
+
+void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, int id)
+{
+ struct script_data vd = ScriptDataInt{id};
+ set_reg(sd, type, reg, vd);
+}
+
+void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, RString zd)
+{
+ struct script_data vd = ScriptDataStr{zd};
+ set_reg(sd, type, reg, vd);
+}
+
+/*==========================================
+ * 文字列への変換
+ *------------------------------------------
+ */
+RString conv_str(ScriptState *st, struct script_data *data)
+{
+ get_val(st, data);
+ assert (!data->is<ScriptDataRetInfo>());
+ if (auto *u = data->get_if<ScriptDataInt>())
+ {
+ AString buf = STRPRINTF("%d"_fmt, u->numi);
+ *data = ScriptDataStr{buf};
+ }
+ return data->get_if<ScriptDataStr>()->str;
+}
+
+/*==========================================
+ * 数値へ変換
+ *------------------------------------------
+ */
+int conv_num(ScriptState *st, struct script_data *data)
+{
+ int rv = 0;
+ get_val(st, data);
+ assert (!data->is<ScriptDataRetInfo>());
+ MATCH (*data)
+ {
+ default:
+ abort();
+ CASE (const ScriptDataStr&, u)
+ {
+ RString p = u.str;
+ rv = atoi(p.c_str());
+ }
+ CASE (const ScriptDataInt&, u)
+ {
+ return u.numi;
+ }
+ CASE (const ScriptDataPos&, u)
+ {
+ return u.numi;
+ }
+ }
+ *data = ScriptDataInt{rv};
+ return rv;
+}
+
+const ScriptBuffer *conv_script(ScriptState *st, struct script_data *data)
+{
+ get_val(st, data);
+ return data->get_if<ScriptDataRetInfo>()->script;
+}
+
+void push_copy(struct script_stack *stack, int pos_)
+{
+ script_data csd = stack->stack_datav[pos_];
+ stack->stack_datav.push_back(csd);
+}
+
+void pop_stack(struct script_stack *stack, int start, int end)
+{
+ auto it = stack->stack_datav.begin();
+ stack->stack_datav.erase(it + start, it + end);
+}
+
+//
+// 実行部main
+//
+/*==========================================
+ * コマンドの読み取り
+ *------------------------------------------
+ */
+static
+ByteCode get_com(ScriptPointer *script)
+{
+ if (static_cast<uint8_t>(script->peek()) >= 0x80)
+ {
+ // synthetic! Does not advance pos yet.
+ return ByteCode::INT;
+ }
+ return script->pop();
+}
+
+/*==========================================
+ * 数値の所得
+ *------------------------------------------
+ */
+static
+int get_num(ScriptPointer *scr)
+{
+ int i = 0;
+ int j = 0;
+ uint8_t val;
+ do
+ {
+ val = static_cast<uint8_t>(scr->pop());
+ i += (val & 0x7f) << j;
+ j += 6;
+ }
+ while (val >= 0xc0);
+ return i;
+}
+
+/*==========================================
+ * スタックから値を取り出す
+ *------------------------------------------
+ */
+static
+int pop_val(ScriptState *st)
+{
+ if (st->stack->stack_datav.empty())
+ return 0;
+ script_data& back = st->stack->stack_datav.back();
+ get_val(st, &back);
+ int rv = 0;
+ if (auto *u = back.get_if<ScriptDataInt>())
+ rv = u->numi;
+ st->stack->stack_datav.pop_back();
+ return rv;
+}
+
+static
+bool isstr(struct script_data& c)
+{
+ return c.is<ScriptDataStr>();
+}
+
+/*==========================================
+ * 加算演算子
+ *------------------------------------------
+ */
+static
+void op_add(ScriptState *st)
+{
+ get_val(st, &st->stack->stack_datav.back());
+ script_data back = st->stack->stack_datav.back();
+ st->stack->stack_datav.pop_back();
+
+ script_data& back1 = st->stack->stack_datav.back();
+ get_val(st, &back1);
+
+ if (!(isstr(back) || isstr(back1)))
+ {
+ back1.get_if<ScriptDataInt>()->numi += back.get_if<ScriptDataInt>()->numi;
+ }
+ else
+ {
+ RString sb = conv_str(st, &back);
+ RString sb1 = conv_str(st, &back1);
+ MString buf;
+ buf += sb1;
+ buf += sb;
+ back1 = ScriptDataStr{.str= AString(buf)};
+ }
+}
+
+/*==========================================
+ * 二項演算子(文字列)
+ *------------------------------------------
+ */
+static
+void op_2str(ScriptState *st, ByteCode op, ZString s1, ZString s2)
+{
+ int a = 0;
+
+ switch (op)
+ {
+ case ByteCode::EQ:
+ a = s1 == s2;
+ break;
+ case ByteCode::NE:
+ a = s1 != s2;
+ break;
+ case ByteCode::GT:
+ a = s1 > s2;
+ break;
+ case ByteCode::GE:
+ a = s1 >= s2;
+ break;
+ case ByteCode::LT:
+ a = s1 < s2;
+ break;
+ case ByteCode::LE:
+ a = s1 <= s2;
+ break;
+ default:
+ PRINTF("illegal string operater\n"_fmt);
+ break;
+ }
+
+ push_int<ScriptDataInt>(st->stack, a);
+}
+
+/*==========================================
+ * 二項演算子(数値)
+ *------------------------------------------
+ */
+static
+void op_2num(ScriptState *st, ByteCode op, int i1, int i2)
+{
+ switch (op)
+ {
+ case ByteCode::SUB:
+ i1 -= i2;
+ break;
+ case ByteCode::MUL:
+ i1 *= i2;
+ break;
+ case ByteCode::DIV:
+ i1 /= i2;
+ break;
+ case ByteCode::MOD:
+ i1 %= i2;
+ break;
+ case ByteCode::AND:
+ i1 &= i2;
+ break;
+ case ByteCode::OR:
+ i1 |= i2;
+ break;
+ case ByteCode::XOR:
+ i1 ^= i2;
+ break;
+ case ByteCode::LAND:
+ i1 = i1 && i2;
+ break;
+ case ByteCode::LOR:
+ i1 = i1 || i2;
+ break;
+ case ByteCode::EQ:
+ i1 = i1 == i2;
+ break;
+ case ByteCode::NE:
+ i1 = i1 != i2;
+ break;
+ case ByteCode::GT:
+ i1 = i1 > i2;
+ break;
+ case ByteCode::GE:
+ i1 = i1 >= i2;
+ break;
+ case ByteCode::LT:
+ i1 = i1 < i2;
+ break;
+ case ByteCode::LE:
+ i1 = i1 <= i2;
+ break;
+ case ByteCode::R_SHIFT:
+ i1 = i1 >> i2;
+ break;
+ case ByteCode::L_SHIFT:
+ i1 = i1 << i2;
+ break;
+ }
+ push_int<ScriptDataInt>(st->stack, i1);
+}
+
+/*==========================================
+ * 二項演算子
+ *------------------------------------------
+ */
+static
+void op_2(ScriptState *st, ByteCode op)
+{
+ // pop_val has unfortunate implications here
+ script_data d2 = st->stack->stack_datav.back();
+ st->stack->stack_datav.pop_back();
+ get_val(st, &d2);
+ script_data d1 = st->stack->stack_datav.back();
+ st->stack->stack_datav.pop_back();
+ get_val(st, &d1);
+
+ if (isstr(d1) && isstr(d2))
+ {
+ // ss => op_2str
+ op_2str(st, op, d1.get_if<ScriptDataStr>()->str, d2.get_if<ScriptDataStr>()->str);
+ }
+ else if (!(isstr(d1) || isstr(d2)))
+ {
+ // ii => op_2num
+ op_2num(st, op, d1.get_if<ScriptDataInt>()->numi, d2.get_if<ScriptDataInt>()->numi);
+ }
+ else
+ {
+ // si,is => error
+ PRINTF("script: op_2: int&str, str&int not allow.\n"_fmt);
+ push_int<ScriptDataInt>(st->stack, 0);
+ }
+}
+
+/*==========================================
+ * 単項演算子
+ *------------------------------------------
+ */
+static
+void op_1num(ScriptState *st, ByteCode op)
+{
+ int i1;
+ i1 = pop_val(st);
+ switch (op)
+ {
+ case ByteCode::NEG:
+ i1 = -i1;
+ break;
+ case ByteCode::NOT:
+ i1 = ~i1;
+ break;
+ case ByteCode::LNOT:
+ i1 = !i1;
+ break;
+ }
+ push_int<ScriptDataInt>(st->stack, i1);
+}
+
+/*==========================================
+ * 関数の実行
+ *------------------------------------------
+ */
+void run_func(ScriptState *st)
+{
+ size_t end_sp = st->stack->stack_datav.size();
+ size_t start_sp = end_sp - 1;
+ while (!st->stack->stack_datav[start_sp].is<ScriptDataArg>())
+ {
+ start_sp--;
+ if (start_sp == 0)
+ {
+ if (battle_config.error_log)
+ PRINTF("function not found\n"_fmt);
+ st->state = ScriptEndState::END;
+ return;
+ }
+ }
+ // the func is before the arg
+ start_sp--;
+ st->start = start_sp;
+ st->end = end_sp;
+
+ if (!st->stack->stack_datav[st->start].is<ScriptDataFuncRef>())
+ {
+ PRINTF("run_func: not function and command! \n"_fmt);
+ st->state = ScriptEndState::END;
+ return;
+ }
+ size_t func = st->stack->stack_datav[st->start].get_if<ScriptDataFuncRef>()->numi;
+
+ if (DEBUG_RUN && battle_config.etc_log)
+ {
+ PRINTF("run_func : %s\n"_fmt,
+ builtin_functions[func].name);
+ PRINTF("stack dump :"_fmt);
+ for (script_data& d : st->stack->stack_datav)
+ {
+ MATCH (d)
+ {
+ CASE (const ScriptDataInt&, u)
+ {
+ PRINTF(" int(%d)"_fmt, u.numi);
+ }
+ CASE (const ScriptDataRetInfo&, u)
+ {
+ PRINTF(" retinfo(%p)"_fmt, static_cast<const void *>(u.script));
+ }
+ CASE (const ScriptDataParam&, u)
+ {
+ PRINTF(" param(%d)"_fmt, u.reg.sp());
+ }
+ CASE (const ScriptDataVariable&, u)
+ {
+ PRINTF(" name(%s)[%d]"_fmt, variable_names.outtern(u.reg.base()), u.reg.index());
+ }
+ CASE (const ScriptDataArg&, u)
+ {
+ (void)u;
+ PRINTF(" arg"_fmt);
+ }
+ CASE (const ScriptDataPos&, u)
+ {
+ (void)u;
+ PRINTF(" pos(%d)"_fmt, u.numi);
+ }
+ CASE (const ScriptDataStr&, u)
+ {
+ (void)u;
+ PRINTF(" str(%s)"_fmt, u.str);
+ }
+ CASE (const ScriptDataFuncRef&, u)
+ {
+ (void)u;
+ PRINTF(" func(%s)"_fmt, builtin_functions[u.numi].name);
+ }
+ }
+ }
+ PRINTF("\n"_fmt);
+ }
+ builtin_functions[func].func(st);
+
+ pop_stack(st->stack, start_sp, end_sp);
+
+ if (st->state == ScriptEndState::RETFUNC)
+ {
+ // ユーザー定義関数からの復帰
+ int olddefsp = st->defsp;
+
+ pop_stack(st->stack, st->defsp, start_sp); // 復帰に邪魔なスタック削除
+ if (st->defsp < 4
+ || !st->stack->stack_datav[st->defsp - 1].is<ScriptDataRetInfo>())
+ {
+ PRINTF("script:run_func (return) return without callfunc or callsub!\n"_fmt);
+ st->state = ScriptEndState::END;
+ return;
+ }
+ assert (olddefsp == st->defsp); // pretty sure it hasn't changed yet
+ st->scriptp.code = conv_script(st, &st->stack->stack_datav[olddefsp - 1]); // スクリプトを復元
+ st->scriptp.pos = conv_num(st, &st->stack->stack_datav[olddefsp - 2]); // スクリプト位置の復元
+ st->defsp = conv_num(st, &st->stack->stack_datav[olddefsp - 3]); // 基準スタックポインタを復元
+ // Number of arguments.
+ int i = conv_num(st, &st->stack->stack_datav[olddefsp - 4]); // 引数の数所得
+ assert (i == 0);
+
+ pop_stack(st->stack, olddefsp - 4 - i, olddefsp); // 要らなくなったスタック(引数と復帰用データ)削除
+
+ st->state = ScriptEndState::GOTO;
+ }
+}
+
+// pretend it's external so this can be called in the debugger
+void dump_script(const ScriptBuffer *script);
+void dump_script(const ScriptBuffer *script)
+{
+ ScriptPointer scriptp(script, 0);
+ while (scriptp.pos < reinterpret_cast<const std::vector<ByteCode> *>(script)->size())
+ {
+ PRINTF("%6zu: "_fmt, scriptp.pos);
+ switch (ByteCode c = get_com(&scriptp))
+ {
+ case ByteCode::EOL:
+ PRINTF("EOL\n"_fmt); // extra newline between functions
+ break;
+ case ByteCode::INT:
+ // synthesized!
+ PRINTF("INT %d"_fmt, get_num(&scriptp));
+ break;
+
+ case ByteCode::POS:
+ case ByteCode::VARIABLE:
+ case ByteCode::FUNC_REF:
+ case ByteCode::PARAM:
+ {
+ int arg = 0;
+ arg |= static_cast<uint8_t>(scriptp.pop()) << 0;
+ arg |= static_cast<uint8_t>(scriptp.pop()) << 8;
+ arg |= static_cast<uint8_t>(scriptp.pop()) << 16;
+ switch(c)
+ {
+ case ByteCode::POS:
+ PRINTF("POS %d"_fmt, arg);
+ break;
+ case ByteCode::VARIABLE:
+ PRINTF("VARIABLE %s"_fmt, variable_names.outtern(arg));
+ break;
+ case ByteCode::FUNC_REF:
+ PRINTF("FUNC_REF %s"_fmt, builtin_functions[arg].name);
+ break;
+ case ByteCode::PARAM:
+ PRINTF("PARAM SP::#%d (sorry)"_fmt, arg);
+ break;
+ }
+ }
+ break;
+ case ByteCode::ARG:
+ PRINTF("ARG"_fmt);
+ break;
+ case ByteCode::STR:
+ PRINTF("STR \"%s\""_fmt, scriptp.pops());
+ break;
+ case ByteCode::FUNC:
+ PRINTF("FUNC"_fmt);
+ break;
+
+ case ByteCode::ADD:
+ PRINTF("ADD"_fmt);
+ break;
+ case ByteCode::SUB:
+ PRINTF("SUB"_fmt);
+ break;
+ case ByteCode::MUL:
+ PRINTF("MUL"_fmt);
+ break;
+ case ByteCode::DIV:
+ PRINTF("DIV"_fmt);
+ break;
+ case ByteCode::MOD:
+ PRINTF("MOD"_fmt);
+ break;
+ case ByteCode::EQ:
+ PRINTF("EQ"_fmt);
+ break;
+ case ByteCode::NE:
+ PRINTF("NE"_fmt);
+ break;
+ case ByteCode::GT:
+ PRINTF("GT"_fmt);
+ break;
+ case ByteCode::GE:
+ PRINTF("GE"_fmt);
+ break;
+ case ByteCode::LT:
+ PRINTF("LT"_fmt);
+ break;
+ case ByteCode::LE:
+ PRINTF("LE"_fmt);
+ break;
+ case ByteCode::AND:
+ PRINTF("AND"_fmt);
+ break;
+ case ByteCode::OR:
+ PRINTF("OR"_fmt);
+ break;
+ case ByteCode::XOR:
+ PRINTF("XOR"_fmt);
+ break;
+ case ByteCode::LAND:
+ PRINTF("LAND"_fmt);
+ break;
+ case ByteCode::LOR:
+ PRINTF("LOR"_fmt);
+ break;
+ case ByteCode::R_SHIFT:
+ PRINTF("R_SHIFT"_fmt);
+ break;
+ case ByteCode::L_SHIFT:
+ PRINTF("L_SHIFT"_fmt);
+ break;
+ case ByteCode::NEG:
+ PRINTF("NEG"_fmt);
+ break;
+ case ByteCode::NOT:
+ PRINTF("NOT"_fmt);
+ break;
+ case ByteCode::LNOT:
+ PRINTF("LNOT"_fmt);
+ break;
+
+ case ByteCode::NOP:
+ PRINTF("NOP"_fmt);
+ break;
+
+ default:
+ PRINTF("??? %d"_fmt, c);
+ break;
+ }
+ PRINTF("\n"_fmt);
+ }
+}
+
+/*==========================================
+ * スクリプトの実行メイン部分
+ *------------------------------------------
+ */
+static
+void run_script_main(ScriptState *st, const ScriptBuffer *rootscript)
+{
+ int cmdcount = script_config.check_cmdcount;
+ int gotocount = script_config.check_gotocount;
+ struct script_stack *stack = st->stack;
+
+ st->defsp = stack->stack_datav.size();
+
+ int rerun_pos = st->scriptp.pos;
+ st->state = ScriptEndState::ZERO;
+ while (st->state == ScriptEndState::ZERO)
+ {
+ switch (ByteCode c = get_com(&st->scriptp))
+ {
+ case ByteCode::EOL:
+ if (stack->stack_datav.size() != st->defsp)
+ {
+ if (true)
+ PRINTF("stack.sp (%zu) != default (%d)\n"_fmt,
+ stack->stack_datav.size(),
+ st->defsp);
+ abort();
+ }
+ rerun_pos = st->scriptp.pos;
+ break;
+ case ByteCode::INT:
+ // synthesized!
+ push_int<ScriptDataInt>(stack, get_num(&st->scriptp));
+ break;
+
+ case ByteCode::POS:
+ case ByteCode::VARIABLE:
+ case ByteCode::FUNC_REF:
+ case ByteCode::PARAM:
+ // Note that these 3 have *very* different meanings,
+ // despite being encoded similarly.
+ {
+ int arg = 0;
+ arg |= static_cast<uint8_t>(st->scriptp.pop()) << 0;
+ arg |= static_cast<uint8_t>(st->scriptp.pop()) << 8;
+ arg |= static_cast<uint8_t>(st->scriptp.pop()) << 16;
+ switch(c)
+ {
+ case ByteCode::POS:
+ push_int<ScriptDataPos>(stack, arg);
+ break;
+ case ByteCode::VARIABLE:
+ push_reg<ScriptDataVariable>(stack, SIR::from(arg));
+ break;
+ case ByteCode::FUNC_REF:
+ push_int<ScriptDataFuncRef>(stack, arg);
+ break;
+ case ByteCode::PARAM:
+ SP arg_sp = static_cast<SP>(arg);
+ push_reg<ScriptDataParam>(stack, SIR::from(arg_sp));
+ break;
+ }
+ }
+ break;
+ case ByteCode::ARG:
+ push_int<ScriptDataArg>(stack, 0);
+ break;
+ case ByteCode::STR:
+ push_str<ScriptDataStr>(stack, st->scriptp.pops());
+ break;
+ case ByteCode::FUNC:
+ run_func(st);
+ if (st->state == ScriptEndState::GOTO)
+ {
+ rerun_pos = st->scriptp.pos;
+ st->state = ScriptEndState::ZERO;
+ if (gotocount > 0 && (--gotocount) <= 0)
+ {
+ PRINTF("run_script: infinity loop !\n"_fmt);
+ st->state = ScriptEndState::END;
+ }
+ }
+ break;
+
+ case ByteCode::ADD:
+ op_add(st);
+ break;
+
+ case ByteCode::SUB:
+ case ByteCode::MUL:
+ case ByteCode::DIV:
+ case ByteCode::MOD:
+ case ByteCode::EQ:
+ case ByteCode::NE:
+ case ByteCode::GT:
+ case ByteCode::GE:
+ case ByteCode::LT:
+ case ByteCode::LE:
+ case ByteCode::AND:
+ case ByteCode::OR:
+ case ByteCode::XOR:
+ case ByteCode::LAND:
+ case ByteCode::LOR:
+ case ByteCode::R_SHIFT:
+ case ByteCode::L_SHIFT:
+ op_2(st, c);
+ break;
+
+ case ByteCode::NEG:
+ case ByteCode::NOT:
+ case ByteCode::LNOT:
+ op_1num(st, c);
+ break;
+
+ case ByteCode::NOP:
+ st->state = ScriptEndState::END;
+ break;
+
+ default:
+ if (battle_config.error_log)
+ PRINTF("unknown command : %d @ %zu\n"_fmt,
+ c, st->scriptp.pos);
+ st->state = ScriptEndState::END;
+ break;
+ }
+ if (cmdcount > 0 && (--cmdcount) <= 0)
+ {
+ PRINTF("run_script: infinity loop !\n"_fmt);
+ st->state = ScriptEndState::END;
+ }
+ }
+ switch (st->state)
+ {
+ case ScriptEndState::STOP:
+ break;
+ case ScriptEndState::END:
+ {
+ dumb_ptr<map_session_data> sd = map_id2sd(st->rid);
+ st->scriptp.code = nullptr;
+ st->scriptp.pos = -1;
+ if (sd && sd->npc_id == st->oid)
+ npc_event_dequeue(sd);
+ }
+ break;
+ case ScriptEndState::RERUNLINE:
+ st->scriptp.pos = rerun_pos;
+ break;
+ }
+
+ if (st->state != ScriptEndState::END)
+ {
+ // 再開するためにスタック情報を保存
+ dumb_ptr<map_session_data> sd = map_id2sd(st->rid);
+ if (sd)
+ {
+ sd->npc_stackbuf = stack->stack_datav;
+ sd->npc_script = st->scriptp.code;
+ // sd->npc_pos is set later ... ???
+ sd->npc_scriptroot = rootscript;
+ }
+ }
+}
+
+/*==========================================
+ * スクリプトの実行
+ *------------------------------------------
+ */
+int run_script(ScriptPointer sp, BlockId rid, BlockId oid)
+{
+ return run_script_l(sp, rid, oid, nullptr);
+}
+
+int run_script_l(ScriptPointer sp, BlockId rid, BlockId oid,
+ Slice<argrec_t> args)
+{
+ struct script_stack stack;
+ ScriptState st;
+ dumb_ptr<map_session_data> sd = map_id2sd(rid);
+ const ScriptBuffer *rootscript = sp.code;
+ int i;
+ if (sp.code == nullptr || sp.pos >> 24)
+ return -1;
+
+ if (sd && !sd->npc_stackbuf.empty() && sd->npc_scriptroot == rootscript)
+ {
+ // 前回のスタックを復帰
+ sp.code = sd->npc_script;
+ stack.stack_datav = std::move(sd->npc_stackbuf);
+ }
+ st.stack = &stack;
+ st.scriptp = sp;
+ st.rid = rid;
+ st.oid = oid;
+ for (i = 0; i < args.size(); i++)
+ {
+ if (args[i].name.back() == '$')
+ pc_setregstr(sd, SIR::from(variable_names.intern(args[i].name)), args[i].v.s);
+ else
+ pc_setreg(sd, SIR::from(variable_names.intern(args[i].name)), args[i].v.i);
+ }
+ run_script_main(&st, rootscript);
+
+ stack.stack_datav.clear();
+ return st.scriptp.pos;
+}
+
+void set_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e, int val)
+{
+ size_t k = variable_names.intern(var);
+ SIR reg = SIR::from(k, e);
+ set_reg(sd, VariableCode::VARIABLE, reg, val);
+}
+void set_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e, XString val)
+{
+ size_t k = variable_names.intern(var);
+ SIR reg = SIR::from(k, e);
+ set_reg(sd, VariableCode::VARIABLE, reg, val);
+}
+int get_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e)
+{
+ size_t k = variable_names.intern(var);
+ SIR reg = SIR::from(k, e);
+ struct script_data dat = ScriptDataVariable{.reg= reg};
+ get_val(sd, &dat);
+ if (auto *u = dat.get_if<ScriptDataInt>())
+ return u->numi;
+ PRINTF("Warning: you lied about the type and I'm too lazy to fix it!"_fmt);
+ return 0;
+}
+ZString get_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e)
+{
+ size_t k = variable_names.intern(var);
+ SIR reg = SIR::from(k, e);
+ struct script_data dat = ScriptDataVariable{.reg= reg};
+ get_val(sd, &dat);
+ if (auto *u = dat.get_if<ScriptDataStr>())
+ // this is almost certainly a memory leak after CONSTSTR removal
+ return u->str;
+ PRINTF("Warning: you lied about the type and I can't fix it!"_fmt);
+ return ZString();
+}
+} // namespace tmwa