summaryrefslogtreecommitdiff
path: root/src/map/script-fun.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/script-fun.cpp')
-rw-r--r--src/map/script-fun.cpp1399
1 files changed, 1306 insertions, 93 deletions
diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp
index 07076d5..beabef3 100644
--- a/src/map/script-fun.cpp
+++ b/src/map/script-fun.cpp
@@ -47,10 +47,10 @@
#include "globals.hpp"
#include "intif.hpp"
#include "itemdb.hpp"
-#include "magic-interpreter-base.hpp"
#include "map.hpp"
#include "mob.hpp"
#include "npc.hpp"
+#include "npc-parse.hpp"
#include "party.hpp"
#include "pc.hpp"
#include "script-call-internal.hpp"
@@ -58,6 +58,8 @@
#include "script-persist.hpp"
#include "skill.hpp"
#include "storage.hpp"
+#include "npc-internal.hpp"
+#include "path.hpp"
#include "../poison.hpp"
@@ -70,6 +72,13 @@ namespace map
#define AARG(n) (st->stack->stack_datav[st->start + 2 + (n)])
#define HARG(n) (st->end > st->start + 2 + (n))
+enum class MonsterAttitude
+{
+ HOSTILE = 0,
+ FRIENDLY = 1,
+ SERVANT = 2,
+ FROZEN = 3,
+};
//
// 埋め込み関数
//
@@ -80,8 +89,16 @@ namespace map
static
void builtin_mes(ScriptState *st)
{
- RString mes = conv_str(st, &AARG(0));
- clif_scriptmes(script_rid2sd(st), st->oid, mes);
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ sd->state.npc_dialog_mes = 1;
+ RString mes = HARG(0) ? conv_str(st, &AARG(0)) : ""_s;
+ clif_scriptmes(sd, st->oid, mes);
+}
+
+static
+void builtin_clear(ScriptState *st)
+{
+ clif_npc_action(script_rid2sd(st), st->oid, 9, 0, 0, 0);
}
/*==========================================
@@ -122,7 +139,6 @@ void builtin_callfunc(ScriptState *st)
for (int i = st->start + 3; i < st->end; i++, j++)
push_copy(st->stack, i);
#endif
-
push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ
push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ
push_int<ScriptDataInt>(st->stack, st->scriptp.pos); // 現在のスクリプト位置をプッシュ
@@ -142,6 +158,98 @@ void builtin_callfunc(ScriptState *st)
OMATCH_END ();
}
+static
+void builtin_call(ScriptState *st)
+{
+ struct script_data *sdata = &AARG(0);
+ get_val(st, sdata);
+ RString str;
+ if (sdata->is<ScriptDataStr>())
+ {
+ str = conv_str(st, sdata);
+ Option<P<const ScriptBuffer>> scr_ = userfunc_db.get(str);
+ OMATCH_BEGIN (scr_)
+ {
+ OMATCH_CASE_SOME (scr)
+ {
+ int j = 0;
+
+ for (int i = st->start + 3; i < st->end; i++, j++)
+ push_copy(st->stack, i);
+
+ push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ
+ push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ
+ push_int<ScriptDataInt>(st->stack, st->scriptp.pos); // 現在のスクリプト位置をプッシュ
+ push_script<ScriptDataRetInfo>(st->stack, TRY_UNWRAP(st->scriptp.code, abort())); // 現在のスクリプトをプッシュ
+
+ st->scriptp = ScriptPointer(scr, 0);
+ st->defsp = st->start + 4 + j;
+ st->state = ScriptEndState::GOTO;
+ return;
+ }
+ OMATCH_CASE_NONE ()
+ {
+ PRINTF("fatal: script: callfunc: function not found! [%s]\n"_fmt, str);
+ st->state = ScriptEndState::END;
+ abort();
+ }
+ }
+ OMATCH_END ();
+ }
+ else
+ {
+ int pos_ = conv_num(st, &AARG(0));
+ int j = 0;
+
+ for (int i = st->start + 3; i < st->end; i++, j++)
+ push_copy(st->stack, i);
+
+ push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ
+ push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ
+ push_int<ScriptDataInt>(st->stack, st->scriptp.pos); // 現在のスクリプト位置をプッシュ
+ push_script<ScriptDataRetInfo>(st->stack, TRY_UNWRAP(st->scriptp.code, abort())); // 現在のスクリプトをプッシュ
+
+ st->scriptp.pos = pos_;
+ st->defsp = st->start + 4 + j;
+ st->state = ScriptEndState::GOTO;
+ }
+
+}
+
+static
+void builtin_getarg(ScriptState *st)
+{
+ int arg = conv_num(st, &AARG(0));
+ if(st->defsp < 1 || !(st->stack->stack_datav[st->defsp - 1].is<ScriptDataRetInfo>()))
+ {
+ dumb_ptr<npc_data> nd = map_id_is_npc(st->oid);
+ if(nd)
+ PRINTF("builtin_getarg: no callfunc or callsub! @ %s\n"_fmt, nd->name);
+ else
+ PRINTF("builtin_getarg: no callfunc or callsub! (no npc)\n"_fmt);
+ st->state = ScriptEndState::END;
+ return;
+ }
+
+ int i = conv_num(st, &st->stack->stack_datav[st->defsp - 4]); // Number of arguments.
+ if (arg > i || arg < 0 || i == 0)
+ {
+ const char a = 3; // ETX
+ if (HARG(1))
+ push_copy(st->stack, st->start + 3);
+ else
+ push_str<ScriptDataStr>(st->stack, VString<1>(a));
+ return;
+ }
+ push_copy(st->stack, (st->defsp - 4 - i) + arg);
+}
+
+static
+void builtin_void(ScriptState *)
+{
+ return;
+}
+
/*==========================================
* サブルーティンの呼び出し
*------------------------------------------
@@ -182,12 +290,12 @@ void builtin_return(ScriptState *st)
else
PRINTF("Deprecated: return outside of callfunc or callsub! (no npc)\n"_fmt);
}
-#if 0
+
if (HARG(0))
{ // 戻り値有り
push_copy(st->stack, st->start + 2);
}
-#endif
+
st->state = ScriptEndState::RETFUNC;
}
@@ -218,14 +326,22 @@ void builtin_close(ScriptState *st)
PRINTF("Deprecated: close in a callfunc or callsub! (no npc)\n"_fmt);
}
st->state = ScriptEndState::END;
- clif_scriptclose(script_rid2sd(st), st->oid);
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ if (sd->state.npc_dialog_mes)
+ clif_scriptclose(sd, st->oid);
+ else
+ clif_npc_action(sd, st->oid, 5, 0, 0, 0);
}
static
void builtin_close2(ScriptState *st)
{
st->state = ScriptEndState::STOP;
- clif_scriptclose(script_rid2sd(st), st->oid);
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ if (sd->state.npc_dialog_mes)
+ clif_scriptclose(sd, st->oid);
+ else
+ clif_npc_action(sd, st->oid, 5, 0, 0, 0);
}
/*==========================================
@@ -252,7 +368,6 @@ void builtin_menu(ScriptState *st)
buf += choice_str;
buf += ':';
}
-
clif_scriptmenu(script_rid2sd(st), st->oid, AString(buf));
}
else
@@ -357,14 +472,43 @@ void builtin_max(ScriptState *st)
static
void builtin_min(ScriptState *st)
{
- int min, num;
- min = conv_num(st, &AARG(0));
+ int min = 0xFFFFFFF6, num;
- for (int i = 1; HARG(i); i++)
+ if (HARG(1))
+ {
+ min = conv_num(st, &AARG(0));
+ for (int i = 1; HARG(i); i++)
+ {
+ num = conv_num(st, &AARG(i));
+ if (num < min)
+ min = num;
+ }
+ }
+ else
{
- num = conv_num(st, &AARG(i));
- if (num < min)
- min = num;
+ SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
+ ZString name = variable_names.outtern(reg.base());
+ char prefix = name.front();
+ if (prefix != '$' && prefix != '@' && prefix != '.')
+ {
+ PRINTF("builtin_max: illegal scope!\n"_fmt);
+ return;
+ }
+ for (int i = reg.index(); i < 256; i++)
+ {
+ struct script_data vd = get_val2(st, reg.iplus(i));
+ MATCH_BEGIN (vd)
+ {
+ MATCH_CASE (const ScriptDataInt&, u)
+ {
+ if (u.numi < min)
+ min = u.numi;
+ continue;
+ }
+ }
+ MATCH_END ();
+ abort();
+ }
}
push_int<ScriptDataInt>(st->stack, min);
@@ -503,6 +647,118 @@ void builtin_heal(ScriptState *st)
*------------------------------------------
*/
static
+void builtin_distance(ScriptState *st)
+{
+ dumb_ptr<block_list> source = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(0))));
+ dumb_ptr<block_list> target = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))));
+ int distance = 0;
+ int mode = HARG(2) ? conv_num(st, &AARG(2)) : 0;
+
+ switch (mode)
+ {
+ // TODO implement case 1 (walk distance)
+ case 0:
+ default:
+ if (source->bl_m != target->bl_m)
+ {
+ // FIXME make it work even if source and target are not in the same map
+ distance = 0x7fffffff;
+ break;
+ }
+ int dx = abs(source->bl_x - target->bl_x);
+ int dy = abs(source->bl_y - target->bl_y);
+ distance = sqrt((dx * dx) + (dy * dy)); // Pythagoras' theorem
+ }
+
+ push_int<ScriptDataInt>(st->stack, distance);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_target(ScriptState *st)
+{
+ // TODO maybe scrap all this and make it use battle_ functions? (add missing functions to battle)
+
+ dumb_ptr<block_list> source = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(0))));
+ dumb_ptr<block_list> target = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))));
+ int flag = conv_num(st, &AARG(2));
+ int val = 0;
+
+ if (flag & 0x01)
+ {
+ int x0 = source->bl_x - AREA_SIZE;
+ int y0 = source->bl_y - AREA_SIZE;
+ int x1 = source->bl_x + AREA_SIZE;
+ int y1 = source->bl_y + AREA_SIZE;
+ if (target->bl_x >= x0 && target->bl_x <= x1 && target->bl_y >= y0 && target->bl_y <= y1)
+ val |= 0x01; // 0x01 target is in visible range
+ }
+
+ if (flag & 0x02)
+ {
+ int range = battle_get_range(source);
+ int x2 = source->bl_x - range;
+ int y2 = source->bl_y - range;
+ int x3 = source->bl_x + range;
+ int y3 = source->bl_y + range;
+ if (target->bl_x >= x2 && target->bl_x <= x3 && target->bl_y >= y2 && target->bl_y <= y3)
+ val |= 0x02; // 0x02 target is in attack range
+ }
+
+ if (flag & 0x04)
+ {
+ struct walkpath_data wpd;
+ if (!path_search(&wpd, source->bl_m, source->bl_x, source->bl_y, target->bl_x, target->bl_y, 0))
+ val |= 0x04; // 0x04 target is walkable (has clear path to target)
+ }
+
+ // TODO 0x08 target is visible (not behind collision)
+
+ if (flag & 0x10)
+ {
+ if (target->bl_type != BL::PC || (target->bl_type == BL::PC &&
+ (target->bl_m->flag.get(MapFlag::PVP) || pc_iskiller(source->is_player(), target->is_player()))))
+ val |= 0x10; // 0x10 target can be attacked by source (killer, killable and so on)
+ }
+
+ if (flag & 0x20)
+ {
+ if (battle_check_range(source, target, 0))
+ val |= 0x20; // 0x20 target is in line of sight
+ }
+
+ push_int<ScriptDataInt>(st->stack, val);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_injure(ScriptState *st)
+{
+ dumb_ptr<block_list> source = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(0))));
+ dumb_ptr<block_list> target = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))));
+ int damage_caused = conv_num(st, &AARG(2));
+
+ // display damage first, because dealing damage may deallocate the target.
+ clif_damage(source, target,
+ gettick(), interval_t::zero(), interval_t::zero(),
+ damage_caused, 0, DamageType::NORMAL);
+
+ battle_damage(source, target, damage_caused, 0);
+
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
void builtin_input(ScriptState *st)
{
dumb_ptr<map_session_data> sd = nullptr;
@@ -549,6 +805,144 @@ void builtin_input(ScriptState *st)
}
}
+static
+void builtin_requestitem(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = nullptr;
+ script_data& scrd = AARG(0);
+ assert (scrd.is<ScriptDataVariable>());
+ int amount = HARG(1) ? conv_num(st, &AARG(1)) : 1;
+ if (amount < 1 || amount > 16)
+ amount = 1;
+
+ SIR reg = scrd.get_if<ScriptDataVariable>()->reg;
+ ZString name = variable_names.outtern(reg.base());
+ char prefix = name.front();
+ char postfix = name.back();
+
+ if (prefix != '$' && prefix != '@' && prefix != '.')
+ {
+ PRINTF("builtin_requestitem: illegal scope!\n"_fmt);
+ abort();
+ }
+
+ sd = script_rid2sd(st);
+ if (sd->state.menu_or_input)
+ {
+ // Second time (rerunline)
+ sd->state.menu_or_input = 0;
+ RString str = sd->npc_str;
+ RString val;
+ const char separator = ';';
+ for (int j = 0; j < amount; j++)
+ {
+ auto find = std::find(str.begin(), str.end(), separator);
+ if (find == str.end())
+ val = str.xislice_h(std::find(str.begin(), str.end(), ','));
+ else
+ {
+ val = str.xislice_h(find);
+ val = val.xislice_h(std::find(val.begin(), val.end(), ','));
+ str = str.xislice_t(find + 1);
+ }
+
+ // check that the item exists in the inventory
+ int num = atoi(val.c_str());
+ if (num < 1)
+ {
+ j--;
+ if (find == str.end())
+ break;
+ continue;
+ }
+ ItemNameId nameid = wrap<ItemNameId>(num);
+ for (IOff0 i : IOff0::iter())
+ if (sd->status.inventory[i].nameid == nameid)
+ goto pass;
+ fail:
+ j--;
+ if (find == str.end())
+ break;
+ continue;
+
+ pass:
+ // push to array
+ if (postfix == '$')
+ {
+ Option<P<struct item_data>> i_data = Some(itemdb_search(nameid));
+ RString item_name = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
+ if (item_name == ""_s)
+ goto fail;
+ if (name.startswith(".@"_s))
+ {
+ struct script_data vd = script_data(ScriptDataStr{item_name});
+ set_scope_reg(st, reg.iplus(j), &vd);
+ }
+ else
+ set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), item_name);
+ }
+ else
+ {
+ if (name.startswith(".@"_s))
+ {
+ struct script_data vd = script_data(ScriptDataInt{num});
+ set_scope_reg(st, reg.iplus(j), &vd);
+ }
+ else
+ set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), num);
+ }
+ if (find == str.end())
+ break;
+ }
+ }
+ else
+ {
+ // First time - send prompt to client, then wait
+ st->state = ScriptEndState::RERUNLINE;
+ clif_scriptinputstr(sd, st->oid); // send string prompt
+ clif_npc_action(sd, st->oid, 10, amount, 0, 0); // send item request
+ sd->state.menu_or_input = 1;
+ }
+}
+
+static
+void builtin_requestlang(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ script_data& scrd = AARG(0);
+ assert (scrd.is<ScriptDataVariable>());
+ SIR reg = scrd.get_if<ScriptDataVariable>()->reg;
+ ZString name = variable_names.outtern(reg.base());
+ char postfix = name.back();
+
+ if (postfix != '$')
+ {
+ PRINTF("builtin_requestlang: illegal type (expects string)!\n"_fmt);
+ abort();
+ }
+
+ if (sd->state.menu_or_input)
+ {
+ // Second time (rerunline)
+ sd->state.menu_or_input = 0;
+ if (name.startswith(".@"_s))
+ {
+ struct script_data vd = script_data(ScriptDataStr{sd->npc_str});
+ set_scope_reg(st, reg, &vd);
+ }
+ else
+ set_reg(sd, VariableCode::VARIABLE, reg, sd->npc_str);
+ }
+ else
+ {
+ // First time - send prompt to client, then wait
+ st->state = ScriptEndState::RERUNLINE;
+ clif_npc_action(sd, st->oid, 0, 0, 0, 0); // send lang request
+ clif_scriptinputstr(sd, st->oid); // send string prompt
+ sd->state.menu_or_input = 1;
+ }
+}
+
/*==========================================
*
*------------------------------------------
@@ -640,47 +1034,364 @@ void builtin_elif (ScriptState *st)
}
/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_foreach_sub(dumb_ptr<block_list> bl, NpcEvent event, BlockId caster)
+{
+ // call_spell_event_script
+ argrec_t arg[1] =
+ {
+ {"@target_id"_s, static_cast<int32_t>(unwrap<BlockId>(bl->bl_id))},
+ };
+ npc_event_do_l(event, caster, arg);
+}
+static
+void builtin_foreach(ScriptState *st)
+{
+ int x0, y0, x1, y1, bl_num;
+
+ dumb_ptr<block_list> caster = map_id2bl(st->rid);
+ bl_num = conv_num(st, &AARG(0));
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(1))));
+ x0 = conv_num(st, &AARG(2));
+ y0 = conv_num(st, &AARG(3));
+ x1 = conv_num(st, &AARG(4));
+ y1 = conv_num(st, &AARG(5));
+ ZString event_ = ZString(conv_str(st, &AARG(6)));
+ BL block_type;
+ NpcEvent event;
+ extract(event_, &event);
+
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
+
+ switch (bl_num)
+ {
+ case 0:
+ block_type = BL::PC;
+ break;
+ case 1:
+ block_type = BL::NPC;
+ break;
+ case 2:
+ block_type = BL::MOB;
+ break;
+ case 3:
+ block_type = BL::NUL;
+ break;
+ default:
+ return;
+ }
+
+ map_foreachinarea(std::bind(builtin_foreach_sub, ph::_1, event, caster->bl_id),
+ m,
+ x0, y0,
+ x1, y1,
+ block_type);
+}
+/*========================================
+ * Destructs a temp NPC
+ *----------------------------------------
+ */
+static
+void builtin_destroy(ScriptState *st)
+{
+ BlockId id;
+ if (HARG(0))
+ id = wrap<BlockId>(conv_num(st, &AARG(0)));
+ else
+ id = st->oid;
+
+ dumb_ptr<npc_data_script> nd = map_id2bl(id)->is_npc()->is_script();
+ if(!nd)
+ return;
+ //assert(nd->disposable == true); we don't care about it anymore
+ npc_free(nd);
+ if (!HARG(0))
+ st->state = ScriptEndState::END;
+}
+/*========================================
+ * Creates a temp NPC
+ *----------------------------------------
+ */
+
+static
+void builtin_puppet(ScriptState *st)
+{
+ int x, y;
+
+ dumb_ptr<block_list> bl = map_id2bl(st->oid);
+ dumb_ptr<npc_data_script> parent_nd = bl->is_npc()->is_script();
+ dumb_ptr<npc_data_script> nd;
+ nd.new_();
+
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ x = conv_num(st, &AARG(1));
+ y = conv_num(st, &AARG(2));
+ Species sprite = wrap<Species>(static_cast<uint16_t>(conv_num(st, &AARG(4))));
+
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
+
+ nd->bl_prev = nd->bl_next = nullptr;
+ nd->scr.event_needs_map = false;
+
+ // PlayerName::SpellName
+ NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARG(3))));
+ nd->name = npc;
+
+ // Dynamically set location
+ nd->bl_m = m;
+ nd->bl_x = x;
+ nd->bl_y = y;
+ if (HARG(5) && HARG(6))
+ {
+ nd->scr.xs = ((conv_num(st, &AARG(5)) * 2) + 1); // do the same equation as in AST
+ nd->scr.ys = ((conv_num(st, &AARG(6)) * 2) + 1);
+ }
+ nd->bl_id = npc_get_new_npc_id();
+ nd->scr.parent = parent_nd->bl_id;
+ nd->dir = DIR::S;
+ nd->flag = 0;
+ nd->sit = DamageType::STAND;
+ nd->npc_class = sprite;
+ nd->speed = 200_ms;
+ nd->option = Opt0::ZERO;
+ nd->opt1 = Opt1::ZERO;
+ nd->opt2 = Opt2::ZERO;
+ nd->opt3 = Opt3::ZERO;
+ nd->scr.label_listv = parent_nd->scr.label_listv;
+ nd->bl_type = BL::NPC;
+ nd->npc_subtype = NpcSubtype::SCRIPT;
+ npc_script++;
+
+ nd->n = map_addnpc(nd->bl_m, nd);
+
+ map_addblock(nd);
+ clif_spawnnpc(nd);
+
+ register_npc_name(nd);
+
+ for (npc_label_list& el : parent_nd->scr.label_listv)
+ {
+ ScriptLabel lname = el.name;
+ int pos = el.pos;
+
+ if (lname.startswith("On"_s))
+ {
+ struct event_data ev {};
+ ev.nd = nd;
+ ev.pos = pos;
+ NpcEvent buf;
+ buf.npc = nd->name;
+ buf.label = lname;
+ ev_db.insert(buf, ev);
+ }
+ }
+
+ for (npc_label_list& el : parent_nd->scr.label_listv)
+ {
+ int t_ = 0;
+ ScriptLabel lname = el.name;
+ int pos = el.pos;
+ if (lname.startswith("OnTimer"_s) && extract(lname.xslice_t(7), &t_) && t_ > 0)
+ {
+ interval_t t = static_cast<interval_t>(t_);
+
+ npc_timerevent_list tel {};
+ tel.timer = t;
+ tel.pos = pos;
+
+ auto it = std::lower_bound(nd->scr.timer_eventv.begin(), nd->scr.timer_eventv.end(), tel,
+ [](const npc_timerevent_list& l, const npc_timerevent_list& r)
+ {
+ return l.timer < r.timer;
+ }
+ );
+ assert (it == nd->scr.timer_eventv.end() || it->timer != tel.timer);
+
+ nd->scr.timer_eventv.insert(it, std::move(tel));
+ }
+ }
+
+ nd->scr.timer = interval_t::zero();
+ nd->scr.next_event = nd->scr.timer_eventv.begin();
+
+ push_int<ScriptDataInt>(st->stack, unwrap<BlockId>(nd->bl_id));
+}
+
+/*==========================================
* 変数設定
*------------------------------------------
*/
static
void builtin_set(ScriptState *st)
{
- dumb_ptr<map_session_data> sd = nullptr;
+ BlockId id;
+ dumb_ptr<block_list> bl = nullptr;
if (auto *u = AARG(0).get_if<ScriptDataParam>())
{
SIR reg = u->reg;
- sd = script_rid2sd(st);
+ if(HARG(2))
+ {
+ struct script_data *sdata = &AARG(2);
+ get_val(st, sdata);
+ CharName name;
+ if (sdata->is<ScriptDataStr>())
+ {
+ name = stringish<CharName>(ZString(conv_str(st, sdata)));
+ if (name.to__actual())
+ bl = map_nick2sd(name);
+ }
+ else
+ {
+ int num = conv_num(st, sdata);
+ if (num >= 2000000)
+ id = wrap<BlockId>(num);
+ else if (num >= 150000)
+ {
+ dumb_ptr<map_session_data> p_sd = nullptr;
+ if ((p_sd = map_nick2sd(map_charid2nick(wrap<CharId>(num)))) != nullptr)
+ id = p_sd->bl_id;
+ else
+ return;
+ }
+ else
+ return;
+ bl = map_id2bl(id);
+ }
+ }
+
+ else
+ bl = script_rid2sd(st)->is_player();
+ if (bl == nullptr)
+ return;
int val = conv_num(st, &AARG(1));
- set_reg(sd, VariableCode::PARAM, reg, val);
+ set_reg(bl, VariableCode::PARAM, reg, val);
return;
}
SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
ZString name = variable_names.outtern(reg.base());
- char prefix = name.front();
- char postfix = name.back();
+ VarName name_ = stringish<VarName>(name);
+ char prefix = name_.front();
+ char postfix = name_.back();
if (prefix != '$')
- sd = script_rid2sd(st);
+ {
+ if(HARG(2))
+ {
+ struct script_data *sdata = &AARG(2);
+ get_val(st, sdata);
+ if(prefix == '.')
+ {
+ if (name_.startswith(".@"_s))
+ {
+ PRINTF("builtin_set: illegal scope!\n"_fmt);
+ return;
+ }
+ NpcName n_name;
+ if (sdata->is<ScriptDataStr>())
+ {
+ n_name = stringish<NpcName>(ZString(conv_str(st, sdata)));
+ bl = npc_name2id(n_name);
+ }
+ else
+ {
+ id = wrap<BlockId>(conv_num(st, sdata));
+ bl = map_id2bl(id);
+ }
+ }
+ else
+ {
+ CharName c_name;
+ if (sdata->is<ScriptDataStr>())
+ {
+ c_name = stringish<CharName>(ZString(conv_str(st, sdata)));
+ if (c_name.to__actual())
+ bl = map_nick2sd(c_name);
+ }
+ else
+ {
+ id = wrap<BlockId>(conv_num(st, sdata));
+ bl = map_id2bl(id);
+ }
+ }
+ }
+ else
+ {
+ if(prefix == '.')
+ {
+ if (name_.startswith(".@"_s))
+ {
+ set_scope_reg(st, reg, &AARG(1));
+ return;
+ }
+ bl = map_id2bl(st->oid)->is_npc();
+ }
+ else
+ bl = map_id2bl(st->rid)->is_player();
+ }
+ if (bl == nullptr)
+ return;
+ }
if (postfix == '$')
{
// 文字列
RString str = conv_str(st, &AARG(1));
- set_reg(sd, VariableCode::VARIABLE, reg, str);
+ set_reg(bl, VariableCode::VARIABLE, reg, str);
}
else
{
// 数値
int val = conv_num(st, &AARG(1));
- set_reg(sd, VariableCode::VARIABLE, reg, val);
+ set_reg(bl, VariableCode::VARIABLE, reg, val);
}
}
+// this is a special function that returns array index for a variable stored in another being
+static
+int getarraysize2(SIR reg, dumb_ptr<block_list> bl)
+{
+ int i = reg.index(), c = i;
+ bool zero = true; // index zero is empty
+ for (; i < 256; i++)
+ {
+ struct script_data vd = ScriptDataVariable{reg.iplus(i)};
+ get_val(bl, &vd);
+ MATCH_BEGIN (vd)
+ {
+ MATCH_CASE (const ScriptDataStr&, u)
+ {
+ if (u.str[0])
+ {
+ if (i == 0)
+ zero = false; // index zero is not empty
+ c = i;
+ }
+ continue;
+ }
+ MATCH_CASE (const ScriptDataInt&, u)
+ {
+ if (u.numi)
+ {
+ if (i == 0)
+ zero = false; // index zero is not empty
+ c = i;
+ }
+ continue;
+ }
+ }
+ MATCH_END ();
+ abort();
+ }
+ return (c == 0 && zero) ? c : (c + 1);
+}
+
/*==========================================
* 配列変数設定
*------------------------------------------
@@ -688,26 +1399,61 @@ void builtin_set(ScriptState *st)
static
void builtin_setarray(ScriptState *st)
{
- dumb_ptr<map_session_data> sd = nullptr;
+ dumb_ptr<block_list> bl = nullptr;
SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
ZString name = variable_names.outtern(reg.base());
char prefix = name.front();
char postfix = name.back();
+ int i = 1, j = 0;
- if (prefix != '$' && prefix != '@')
+ if (prefix != '$' && prefix != '@' && prefix != '.')
{
PRINTF("builtin_setarray: illegal scope!\n"_fmt);
return;
}
- if (prefix != '$')
- sd = script_rid2sd(st);
+ if (prefix == '.' && !name.startswith(".@"_s))
+ {
+ struct script_data *sdata = &AARG(1);
+ get_val(st, sdata);
+ i++; // 2nd argument is npc, not an array element
+ if (sdata->is<ScriptDataStr>())
+ {
+ ZString tn = conv_str(st, sdata);
+ if (tn == "this"_s || tn == "oid"_s)
+ bl = map_id2bl(st->oid)->is_npc();
+ else
+ {
+ NpcName name_ = stringish<NpcName>(tn);
+ bl = npc_name2id(name_);
+ }
+ }
+ else
+ {
+ int tid = conv_num(st, sdata);
+ if (tid == 0)
+ bl = map_id2bl(st->oid)->is_npc();
+ else
+ bl = map_id2bl(wrap<BlockId>(tid))->is_npc();
+ }
+ if (!bl)
+ {
+ PRINTF("builtin_setarray: npc not found\n"_fmt);
+ return;
+ }
+ if (st->oid && bl->bl_id != st->oid)
+ j = getarraysize2(reg, bl);
+ }
+ else if (prefix != '$' && !name.startswith(".@"_s))
+ bl = map_id2bl(st->rid)->is_player();
- for (int j = 0, i = 1; i < st->end - st->start - 2 && j < 256; i++, j++)
+ for (; i < st->end - st->start - 2 && j < 256; i++, j++)
{
- if (postfix == '$')
- set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), conv_str(st, &AARG(i)));
+ if (name.startswith(".@"_s))
+ set_scope_reg(st, reg.iplus(j), &AARG(i));
+ else if (postfix == '$')
+ set_reg(bl, VariableCode::VARIABLE, reg.iplus(j), conv_str(st, &AARG(i)));
else
- set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), conv_num(st, &AARG(i)));
+ set_reg(bl, VariableCode::VARIABLE, reg.iplus(j), conv_num(st, &AARG(i)));
}
}
@@ -718,27 +1464,31 @@ void builtin_setarray(ScriptState *st)
static
void builtin_cleararray(ScriptState *st)
{
- dumb_ptr<map_session_data> sd = nullptr;
+ dumb_ptr<block_list> bl = nullptr;
SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
ZString name = variable_names.outtern(reg.base());
char prefix = name.front();
char postfix = name.back();
int sz = conv_num(st, &AARG(2));
- if (prefix != '$' && prefix != '@')
+ if (prefix != '$' && prefix != '@' && prefix != '.')
{
PRINTF("builtin_cleararray: illegal scope!\n"_fmt);
return;
}
- if (prefix != '$')
- sd = script_rid2sd(st);
+ if (prefix == '.' && !name.startswith(".@"_s))
+ bl = map_id2bl(st->oid)->is_npc();
+ else if (prefix != '$' && !name.startswith(".@"_s))
+ bl = map_id2bl(st->rid)->is_player();
for (int i = 0; i < sz; i++)
{
- if (postfix == '$')
- set_reg(sd, VariableCode::VARIABLE, reg.iplus(i), conv_str(st, &AARG(1)));
+ if (name.startswith(".@"_s))
+ set_scope_reg(st, reg.iplus(i), &AARG(i));
+ else if (postfix == '$')
+ set_reg(bl, VariableCode::VARIABLE, reg.iplus(i), conv_str(st, &AARG(1)));
else
- set_reg(sd, VariableCode::VARIABLE, reg.iplus(i), conv_num(st, &AARG(1)));
+ set_reg(bl, VariableCode::VARIABLE, reg.iplus(i), conv_num(st, &AARG(1)));
}
}
@@ -782,7 +1532,7 @@ void builtin_getarraysize(ScriptState *st)
ZString name = variable_names.outtern(reg.base());
char prefix = name.front();
- if (prefix != '$' && prefix != '@')
+ if (prefix != '$' && prefix != '@' && prefix != '.')
{
PRINTF("builtin_copyarray: illegal scope!\n"_fmt);
return;
@@ -1195,6 +1945,28 @@ void builtin_getcharid(ScriptState *st)
}
/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_getnpcid(ScriptState *st)
+{
+ dumb_ptr<npc_data> nd;
+
+ if (HARG(0))
+ nd = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0)))));
+ else
+ nd = map_id2bl(st->oid)->is_npc();
+ if (nd == nullptr)
+ {
+ push_int<ScriptDataInt>(st->stack, -1);
+ return;
+ }
+
+ push_int<ScriptDataInt>(st->stack, unwrap<BlockId>(nd->bl_id));
+}
+
+/*==========================================
*指定IDのPT名取得
*------------------------------------------
*/
@@ -1216,7 +1988,11 @@ void builtin_strcharinfo(ScriptState *st)
dumb_ptr<map_session_data> sd;
int num;
- sd = script_rid2sd(st);
+ if (HARG(1)) //指定したキャラを状態異常にする
+ sd = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))))->is_player();
+ else
+ sd = script_rid2sd(st);
+
num = conv_num(st, &AARG(0));
if (num == 0)
{
@@ -1267,7 +2043,10 @@ void builtin_getequipid(ScriptState *st)
int num;
dumb_ptr<map_session_data> sd;
- sd = script_rid2sd(st);
+ if (HARG(1))
+ sd = map_nick2sd(stringish<CharName>(ZString(conv_str(st, &AARG(1)))));
+ else
+ sd = script_rid2sd(st);
if (sd == nullptr)
{
PRINTF("getequipid: sd == nullptr\n"_fmt);
@@ -1323,7 +2102,11 @@ void builtin_freeloop(ScriptState *st)
static
void builtin_bonus(ScriptState *st)
{
- SP type = SP(conv_num(st, &AARG(0)));
+ SP type;
+ if (auto *u = AARG(0).get_if<ScriptDataParam>())
+ type = u->reg.sp();
+ else
+ type = SP(conv_num(st, &AARG(0)));
int val = conv_num(st, &AARG(1));
dumb_ptr<map_session_data> sd = script_rid2sd(st);
pc_bonus(sd, type, val);
@@ -1337,7 +2120,11 @@ void builtin_bonus(ScriptState *st)
static
void builtin_bonus2(ScriptState *st)
{
- SP type = SP(conv_num(st, &AARG(0)));
+ SP type;
+ if (auto *u = AARG(0).get_if<ScriptDataParam>())
+ type = u->reg.sp();
+ else
+ type = SP(conv_num(st, &AARG(0)));
int type2 = conv_num(st, &AARG(1));
int val = conv_num(st, &AARG(2));
dumb_ptr<map_session_data> sd = script_rid2sd(st);
@@ -1401,6 +2188,31 @@ void builtin_getskilllv(ScriptState *st)
*------------------------------------------
*/
static
+void builtin_overrideattack(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ int charges = conv_num(st, &AARG(0));
+ interval_t attack_delay = static_cast<interval_t>(conv_num(st, &AARG(1)));
+ int attack_range = conv_num(st, &AARG(2));
+ StatusChange icon = StatusChange(conv_num(st, &AARG(3)));
+ ItemNameId look = wrap<ItemNameId>(static_cast<uint16_t>(conv_num(st, &AARG(4))));
+ ZString event_ = ZString(conv_str(st, &AARG(5)));
+
+ NpcEvent event;
+ extract(event_, &event);
+
+ sd->attack_spell_override = st->oid;
+ sd->attack_spell_charges = charges;
+ sd->magic_attack = event;
+ pc_set_weapon_icon(sd, charges, icon, look);
+ pc_set_attack_info(sd, attack_delay, attack_range);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
void builtin_getgmlevel(ScriptState *st)
{
push_int<ScriptDataInt>(st->stack, pc_isGM(script_rid2sd(st)).get_all_bits());
@@ -1594,6 +2406,72 @@ void builtin_getexp(ScriptState *st)
}
+static
+void builtin_summon(ScriptState *st)
+{
+ NpcEvent event;
+ MapName map = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ int x = conv_num(st, &AARG(1));
+ int y = conv_num(st, &AARG(2));
+ dumb_ptr<block_list> owner_e = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(3))));
+ dumb_ptr<map_session_data> owner = nullptr;
+ Species monster_id = wrap<Species>(conv_num(st, &AARG(4)));
+ MonsterAttitude monster_attitude = static_cast<MonsterAttitude>(conv_num(st, &AARG(5)));
+ interval_t lifespan = static_cast<interval_t>(conv_num(st, &AARG(6)));
+ if (HARG(7))
+ extract(ZString(conv_str(st, &AARG(7))), &event);
+
+ if (monster_attitude == MonsterAttitude::SERVANT
+ && owner_e->bl_type == BL::PC)
+ owner = owner_e->is_player(); // XXX in the future this should also work with mobs as owner
+
+ BlockId mob_id = mob_once_spawn(owner, map, x, y, MobName(), monster_id, 1, event);
+ dumb_ptr<mob_data> mob = map_id_is_mob(mob_id);
+
+ if (mob)
+ {
+ mob->mode = get_mob_db(monster_id).mode;
+
+ switch (monster_attitude)
+ {
+ case MonsterAttitude::SERVANT:
+ mob->state.special_mob_ai = 1;
+ mob->mode |= MobMode::AGGRESSIVE;
+ break;
+
+ case MonsterAttitude::FRIENDLY:
+ mob->mode = MobMode::CAN_ATTACK | (mob->mode & MobMode::CAN_MOVE);
+ break;
+
+ case MonsterAttitude::HOSTILE:
+ mob->mode = MobMode::CAN_ATTACK | MobMode::AGGRESSIVE | (mob->mode & MobMode::CAN_MOVE);
+ if (owner)
+ {
+ mob->target_id = owner->bl_id;
+ mob->attacked_id = owner->bl_id;
+ }
+ break;
+
+ case MonsterAttitude::FROZEN:
+ mob->mode = MobMode::ZERO;
+ break;
+ }
+
+ mob->mode |=
+ MobMode::SUMMONED | MobMode::TURNS_AGAINST_BAD_MASTER;
+
+ mob->deletetimer = Timer(gettick() + lifespan,
+ std::bind(mob_timer_delete, ph::_1, ph::_2,
+ mob_id));
+
+ if (owner)
+ {
+ mob->master_id = owner->bl_id;
+ mob->master_dist = 6;
+ }
+ }
+}
+
/*==========================================
* モンスター発生
*------------------------------------------
@@ -1712,6 +2590,20 @@ void builtin_addtimer(ScriptState *st)
}
/*==========================================
+ * NPCイベントタイマー追加
+ *------------------------------------------
+ */
+static
+void builtin_addnpctimer(ScriptState *st)
+{
+ interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(0)));
+ ZString event_ = ZString(conv_str(st, &AARG(1)));
+ NpcEvent event;
+ extract(event_, &event);
+ npc_addeventtimer(npc_name2id(event.npc), tick, event);
+}
+
+/*==========================================
* NPCタイマー初期化
*------------------------------------------
*/
@@ -1842,6 +2734,48 @@ void builtin_npcaction(ScriptState *st)
}
static
+void builtin_camera(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ if (HARG(0))
+ {
+ if (HARG(1) && !HARG(2))
+ clif_npc_action(sd, st->oid, 2, 0, conv_num(st, &AARG(0)), conv_num(st, &AARG(1))); // camera to x, y
+ else
+ {
+ dumb_ptr<block_list> bl;
+ short x = 0, y = 0;
+ bool rel = false;
+ if (auto *u = AARG(0).get_if<ScriptDataInt>())
+ bl = map_id2bl(wrap<BlockId>(u->numi));
+ if (auto *g = AARG(0).get_if<ScriptDataStr>())
+ {
+ if (g->str == "rid"_s || g->str == "player"_s)
+ bl = sd;
+ if (g->str == "relative"_s)
+ rel = true;
+ else if (g->str == "oid"_s || g->str == "npc"_s)
+ bl = map_id2bl(st->oid);
+ else
+ bl = npc_name2id(stringish<NpcName>(g->str));
+ }
+ if (HARG(1) && HARG(2))
+ {
+ x = conv_num(st, &AARG(1));
+ y = conv_num(st, &AARG(2));
+ }
+ if (rel)
+ clif_npc_action(sd, st->oid, 4, 0, x, y); // camera relative from current camera
+ else
+ clif_npc_action(sd, st->oid, 2, unwrap<BlockId>(bl->bl_id), x, y); // camera to actor
+ }
+ }
+
+ else
+ clif_npc_action(sd, st->oid, 3, 0, 0, 0); // return camera
+}
+
+static
void builtin_setnpcdirection(ScriptState *st)
{
dumb_ptr<npc_data> nd_;
@@ -1981,6 +2915,38 @@ void builtin_getmapusers(ScriptState *st)
push_int<ScriptDataInt>(st->stack, users);
}
+static
+void builtin_aggravate_sub(dumb_ptr<block_list> bl, dumb_ptr<block_list> target, int effect)
+{
+ dumb_ptr<mob_data> md = bl->is_mob();
+
+ if (mob_aggravate(md, target))
+ clif_misceffect(bl, effect);
+}
+
+static
+void builtin_aggravate(ScriptState *st)
+{
+ dumb_ptr<block_list> target = map_id2bl(st->rid);
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ int x0 = conv_num(st, &AARG(1));
+ int y0 = conv_num(st, &AARG(2));
+ int x1 = conv_num(st, &AARG(3));
+ int y1 = conv_num(st, &AARG(4));
+ int effect = conv_num(st, &AARG(5));
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str),
+ {
+ push_int<ScriptDataInt>(st->stack, -1);
+ return;
+ });
+
+ map_foreachinarea(std::bind(builtin_aggravate_sub, ph::_1, target, effect),
+ m,
+ x0, y0,
+ x1, y1,
+ BL::MOB);
+}
+
/*==========================================
* エリア指定ユーザー数所得
*------------------------------------------
@@ -2161,7 +3127,11 @@ void builtin_sc_end(ScriptState *st)
{
dumb_ptr<block_list> bl;
StatusChange type = StatusChange(conv_num(st, &AARG(0)));
- bl = map_id2bl(st->rid);
+ if (HARG(1)) //指定したキャラを状態異常にする
+ bl = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))));
+ else
+ bl = map_id2bl(st->rid);
+
skill_status_change_end(bl, type, nullptr);
}
@@ -2170,7 +3140,10 @@ void builtin_sc_check(ScriptState *st)
{
dumb_ptr<block_list> bl;
StatusChange type = StatusChange(conv_num(st, &AARG(0)));
- bl = map_id2bl(st->rid);
+ if (HARG(1)) //指定したキャラを状態異常にする
+ bl = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))));
+ else
+ bl = map_id2bl(st->rid);
push_int<ScriptDataInt>(st->stack, skill_status_change_active(bl, type));
@@ -2207,10 +3180,17 @@ void builtin_resetstatus(ScriptState *st)
static
void builtin_attachrid(ScriptState *st)
{
- st->rid = wrap<BlockId>(conv_num(st, &AARG(0)));
+ dumb_ptr<map_session_data> sd = map_id2sd(st->rid);
+ BlockId newid = wrap<BlockId>(conv_num(st, &AARG(0)));
+
+ if (sd && newid != st->rid)
+ sd->npc_id = BlockId();
+
+ st->rid = newid;
push_int<ScriptDataInt>(st->stack, (map_id2sd(st->rid) != nullptr));
}
+
/*==========================================
* RIDのデタッチ
*------------------------------------------
@@ -2218,6 +3198,9 @@ void builtin_attachrid(ScriptState *st)
static
void builtin_detachrid(ScriptState *st)
{
+ dumb_ptr<map_session_data> sd = map_id2sd(st->rid);
+ if (sd)
+ sd->npc_id = BlockId();
st->rid = BlockId();
}
@@ -2357,7 +3340,12 @@ void builtin_setpvpchannel(ScriptState *st)
static
void builtin_getpvpflag(ScriptState *st)
{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ dumb_ptr<map_session_data> sd;
+ if (HARG(1)) //指定したキャラを状態異常にする
+ sd = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))))->is_player();
+ else
+ sd = script_rid2sd(st);
+
int num = conv_num(st, &AARG(0));
int flag = 0;
@@ -2508,64 +3496,82 @@ void builtin_getitemlink(ScriptState *st)
}
static
-void builtin_getspellinvocation(ScriptState *st)
+void builtin_getpartnerid2(ScriptState *st)
{
- RString name = conv_str(st, &AARG(0));
-
- AString invocation = magic::magic_find_invocation(name);
- if (!invocation)
- invocation = "..."_s;
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
- push_str<ScriptDataStr>(st->stack, invocation);
+ push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status.partner_id));
}
static
-void builtin_getpartnerid2(ScriptState *st)
+void builtin_chr(ScriptState *st)
{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ const char ascii = conv_num(st, &AARG(0));
+ push_str<ScriptDataStr>(st->stack, VString<1>(ascii));
+}
- push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status.partner_id));
+static
+void builtin_ord(ScriptState *st)
+{
+ const char ascii = conv_str(st, &AARG(0)).front();
+ push_int<ScriptDataInt>(st->stack, static_cast<int>(ascii));
}
static
void builtin_explode(ScriptState *st)
{
- dumb_ptr<map_session_data> sd = nullptr;
+ dumb_ptr<block_list> bl = nullptr;
SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
ZString name = variable_names.outtern(reg.base());
- const char separator = conv_str(st, &AARG(2))[0];
+ const char separator = conv_str(st, &AARG(2)).front();
RString str = conv_str(st, &AARG(1));
RString val;
char prefix = name.front();
char postfix = name.back();
- if (prefix != '$' && prefix != '@')
+ if (prefix != '$' && prefix != '@' && prefix != '.')
{
PRINTF("builtin_explode: illegal scope!\n"_fmt);
return;
}
- if (prefix != '$')
- sd = script_rid2sd(st);
+ if (prefix == '.' && !name.startswith(".@"_s))
+ bl = map_id2bl(st->oid)->is_npc();
+ else if (prefix != '$' && prefix != '.')
+ bl = map_id2bl(st->rid)->is_player();
for (int j = 0; j < 256; j++)
{
auto find = std::find(str.begin(), str.end(), separator);
if (find == str.end())
{
- if (postfix == '$')
- set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), str);
+ if (name.startswith(".@"_s))
+ {
+ struct script_data vd = script_data(ScriptDataInt{atoi(str.c_str())});
+ if (postfix == '$')
+ vd = script_data(ScriptDataStr{str});
+ set_scope_reg(st, reg.iplus(j), &vd);
+ }
+ else if (postfix == '$')
+ set_reg(bl, VariableCode::VARIABLE, reg.iplus(j), str);
else
- set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), atoi(str.c_str()));
+ set_reg(bl, VariableCode::VARIABLE, reg.iplus(j), atoi(str.c_str()));
break;
}
{
val = str.xislice_h(find);
str = str.xislice_t(find + 1);
- if (postfix == '$')
- set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), val);
+ if (name.startswith(".@"_s))
+ {
+ struct script_data vd = script_data(ScriptDataInt{atoi(val.c_str())});
+ if (postfix == '$')
+ vd = script_data(ScriptDataStr{val});
+ set_scope_reg(st, reg.iplus(j), &vd);
+ }
+ else if (postfix == '$')
+ set_reg(bl, VariableCode::VARIABLE, reg.iplus(j), val);
else
- set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), atoi(val.c_str()));
+ set_reg(bl, VariableCode::VARIABLE, reg.iplus(j), atoi(val.c_str()));
}
}
}
@@ -2771,6 +3777,128 @@ void builtin_specialeffect2(ScriptState *st)
}
+static
+void builtin_get(ScriptState *st)
+{
+ BlockId id;
+ dumb_ptr<block_list> bl = nullptr;
+ if (auto *u = AARG(0).get_if<ScriptDataParam>())
+ {
+ SIR reg = u->reg;
+ struct script_data *sdata = &AARG(1);
+ get_val(st, sdata);
+ CharName name;
+ if (sdata->is<ScriptDataStr>())
+ {
+ name = stringish<CharName>(ZString(conv_str(st, sdata)));
+ if (name.to__actual())
+ bl = map_nick2sd(name);
+ }
+ else
+ {
+ int num = conv_num(st, sdata);
+ if (num >= 2000000)
+ id = wrap<BlockId>(num);
+ else if (num >= 150000)
+ {
+ dumb_ptr<map_session_data> p_sd = nullptr;
+ if ((p_sd = map_nick2sd(map_charid2nick(wrap<CharId>(num)))) != nullptr)
+ id = p_sd->bl_id;
+ else
+ return;
+ }
+ else
+ return;
+ bl = map_id2bl(id);
+ }
+
+ if (bl == nullptr)
+ return;
+ int var = pc_readparam(bl, reg.sp());
+ push_int<ScriptDataInt>(st->stack, var);
+ return;
+ }
+
+ struct script_data *sdata = &AARG(1);
+ get_val(st, sdata);
+
+ SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
+ ZString name_ = variable_names.outtern(reg.base());
+ char prefix = name_.front();
+ char postfix = name_.back();
+
+ if(prefix == '.')
+ {
+ if (name_.startswith(".@"_s))
+ {
+ PRINTF("builtin_get: illegal scope!\n"_fmt);
+ return;
+ }
+ NpcName name;
+ if (sdata->is<ScriptDataStr>())
+ {
+ name = stringish<NpcName>(ZString(conv_str(st, sdata)));
+ bl = npc_name2id(name);
+ }
+ else
+ {
+ id = wrap<BlockId>(conv_num(st, sdata));
+ bl = map_id2bl(id);
+ }
+ }
+ else if(prefix != '$')
+ {
+ CharName name;
+ if (sdata->is<ScriptDataStr>())
+ {
+ name = stringish<CharName>(ZString(conv_str(st, sdata)));
+ if (name.to__actual())
+ bl = map_nick2sd(name);
+ }
+ else
+ {
+ id = wrap<BlockId>(conv_num(st, sdata));
+ bl = map_id2bl(id);
+ }
+ }
+ else
+ {
+ PRINTF("builtin_get: illegal scope !\n"_fmt);
+ return;
+ }
+
+ if (!bl)
+ {
+ PRINTF("builtin_get: no block list attached %s!\n"_fmt, conv_str(st, &AARG(1)));
+ if (postfix == '$')
+ push_str<ScriptDataStr>(st->stack, conv_str(st, &AARG(1)));
+ else
+ push_int<ScriptDataInt>(st->stack, 0);
+ return;
+ }
+
+ if (postfix == '$')
+ {
+ ZString var = pc_readregstr(bl, reg);
+ push_str<ScriptDataStr>(st->stack, var);
+ }
+ else
+ {
+ int var;
+ if (prefix == '#' && bl)
+ {
+ if (name_.startswith("##"_s))
+ var = pc_readaccountreg2(bl->is_player(), stringish<VarName>(name_));
+ else
+ var = pc_readaccountreg(bl->is_player(), stringish<VarName>(name_));
+ }
+ else
+ var = pc_readreg(bl, reg);
+
+ push_int<ScriptDataInt>(st->stack, var);
+ }
+}
+
/*==========================================
* Nude [Valaris]
*------------------------------------------
@@ -3069,6 +4197,20 @@ void builtin_npctalk(ScriptState *st)
}
/*==========================================
+ * register cmd
+ *------------------------------------------
+ */
+static
+void builtin_registercmd(ScriptState *st)
+{
+ RString evoke = conv_str(st, &AARG(0));
+ ZString event_ = conv_str(st, &AARG(1));
+ NpcEvent event;
+ extract(event_, &event);
+ spells_by_events.put(evoke, event);
+}
+
+/*==========================================
* getlook char info. getlook(arg)
*------------------------------------------
*/
@@ -3152,31 +4294,56 @@ void builtin_getsavepoint(ScriptState *st)
static
void builtin_areatimer_sub(dumb_ptr<block_list> bl, interval_t tick, NpcEvent event)
{
- pc_addeventtimer(bl->is_player(), tick, event);
+ if (bl->bl_type == BL::PC)
+ {
+ dumb_ptr<map_session_data> sd = map_id_is_player(bl->bl_id);
+ pc_addeventtimer(sd, tick, event);
+ }
+ else
+ {
+ npc_addeventtimer(bl, tick, event);
+ }
}
static
void builtin_areatimer(ScriptState *st)
{
- int x0, y0, x1, y1;
-
- MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
- x0 = conv_num(st, &AARG(1));
- y0 = conv_num(st, &AARG(2));
- x1 = conv_num(st, &AARG(3));
- y1 = conv_num(st, &AARG(4));
- interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(5)));
- ZString event_ = ZString(conv_str(st, &AARG(6)));
+ int x0, y0, x1, y1, bl_num;
+
+ bl_num = conv_num(st, &AARG(0));
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(1))));
+ x0 = conv_num(st, &AARG(2));
+ y0 = conv_num(st, &AARG(3));
+ x1 = conv_num(st, &AARG(4));
+ y1 = conv_num(st, &AARG(5));
+ interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(6)));
+ ZString event_ = conv_str(st, &AARG(7));
+ BL block_type;
NpcEvent event;
extract(event_, &event);
P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
+ switch (bl_num)
+ {
+ case 0:
+ block_type = BL::PC;
+ break;
+ case 1:
+ block_type = BL::NPC;
+ break;
+ case 2:
+ block_type = BL::MOB;
+ break;
+ default:
+ return;
+ }
+
map_foreachinarea(std::bind(builtin_areatimer_sub, ph::_1, tick, event),
m,
x0, y0,
x1, y1,
- BL::PC);
+ block_type);
}
/*==========================================
@@ -3305,6 +4472,18 @@ void builtin_gety(ScriptState *st)
push_int<ScriptDataInt>(st->stack, sd->bl_y);
}
+/*============================
+ * Gets the PC's direction
+ *----------------------------
+ */
+static
+void builtin_getdir(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ push_int<ScriptDataInt>(st->stack, static_cast<uint8_t>(sd->dir));
+}
+
/*
* Get the PC's current map's name
*/
@@ -3327,11 +4506,23 @@ void builtin_strnpcinfo(ScriptState *st)
dumb_ptr<npc_data> nd;
if(HARG(1)){
- NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARG(1))));
- nd = npc_name2id(npc);
+ struct script_data *sdata = &AARG(1);
+ get_val(st, sdata);
+
+ if (sdata->is<ScriptDataStr>())
+ {
+ NpcName name_ = stringish<NpcName>(ZString(conv_str(st, sdata)));
+ nd = npc_name2id(name_);
+ }
+ else
+ {
+ BlockId id = wrap<BlockId>(conv_num(st, sdata));
+ nd = map_id2bl(id)->is_npc();
+ }
+
if (!nd)
{
- PRINTF("builtin_strnpcinfo: no such npc: '%s'\n"_fmt, npc);
+ PRINTF("builtin_strnpcinfo: npc not found\n"_fmt);
return;
}
} else {
@@ -3417,11 +4608,15 @@ void builtin_mapexit(ScriptState *)
BuiltinFunction builtin_functions[] =
{
- BUILTIN(mes, "s"_s, '\0'),
+ BUILTIN(mes, "?"_s, '\0'),
+ BUILTIN(clear, ""_s, '\0'),
BUILTIN(goto, "L"_s, '\0'),
BUILTIN(callfunc, "F"_s, '\0'),
+ BUILTIN(call, "F?*"_s, '.'),
BUILTIN(callsub, "L"_s, '\0'),
- BUILTIN(return, ""_s, '\0'),
+ BUILTIN(getarg, "i?"_s, '.'),
+ BUILTIN(return, "?"_s, '\0'),
+ BUILTIN(void, "?*"_s, '\0'),
BUILTIN(next, ""_s, '\0'),
BUILTIN(close, ""_s, '\0'),
BUILTIN(close2, ""_s, '\0'),
@@ -3431,11 +4626,15 @@ BuiltinFunction builtin_functions[] =
BUILTIN(warp, "Mxy"_s, '\0'),
BUILTIN(areawarp, "MxyxyMxy"_s, '\0'),
BUILTIN(heal, "ii?"_s, '\0'),
+ BUILTIN(injure, "iii"_s, '\0'),
BUILTIN(input, "N"_s, '\0'),
+ BUILTIN(requestitem, "N?"_s, '\0'),
+ BUILTIN(requestlang, "N"_s, '\0'),
BUILTIN(if, "iF*"_s, '\0'),
BUILTIN(elif, "iF*"_s, '\0'),
BUILTIN(else, "F*"_s, '\0'),
- BUILTIN(set, "Ne"_s, '\0'),
+ BUILTIN(set, "Ne?"_s, '\0'),
+ BUILTIN(get, "Ne"_s, '.'),
BUILTIN(setarray, "Ne*"_s, '\0'),
BUILTIN(cleararray, "Nei"_s, '\0'),
BUILTIN(getarraysize, "N"_s, 'i'),
@@ -3448,14 +4647,16 @@ BuiltinFunction builtin_functions[] =
BUILTIN(makeitem, "IiMxy"_s, '\0'),
BUILTIN(delitem, "Ii"_s, '\0'),
BUILTIN(getcharid, "i?"_s, 'i'),
+ BUILTIN(getnpcid, "?"_s, 'i'),
BUILTIN(getversion, ""_s, 'i'),
- BUILTIN(strcharinfo, "i"_s, 's'),
- BUILTIN(getequipid, "i"_s, 'i'),
+ BUILTIN(strcharinfo, "i?"_s, 's'),
+ BUILTIN(getequipid, "i?"_s, 'i'),
BUILTIN(bonus, "ii"_s, '\0'),
BUILTIN(bonus2, "iii"_s, '\0'),
BUILTIN(skill, "ii?"_s, '\0'),
BUILTIN(setskill, "ii"_s, '\0'),
BUILTIN(getskilllv, "i"_s, 'i'),
+ BUILTIN(overrideattack, "iiiiiE"_s, '\0'),
BUILTIN(getgmlevel, ""_s, 'i'),
BUILTIN(end, ""_s, '\0'),
BUILTIN(getopt2, ""_s, 'i'),
@@ -3465,11 +4666,13 @@ BuiltinFunction builtin_functions[] =
BUILTIN(gettime, "i"_s, 'i'),
BUILTIN(openstorage, ""_s, '\0'),
BUILTIN(getexp, "ii"_s, '\0'),
+ BUILTIN(summon, "Mxysmii?"_s, '\0'),
BUILTIN(monster, "Mxysmi?"_s, '\0'),
BUILTIN(areamonster, "Mxyxysmi?"_s, '\0'),
BUILTIN(killmonster, "ME"_s, '\0'),
BUILTIN(donpcevent, "E"_s, '\0'),
BUILTIN(addtimer, "tE"_s, '\0'),
+ BUILTIN(addnpctimer, "tE"_s, '\0'),
BUILTIN(initnpctimer, "?"_s, '\0'),
BUILTIN(startnpctimer, "?"_s, '\0'),
BUILTIN(stopnpctimer, "?"_s, '\0'),
@@ -3477,6 +4680,7 @@ BuiltinFunction builtin_functions[] =
BUILTIN(setnpctimer, "i?"_s, '\0'),
BUILTIN(setnpcdirection, "iii?"_s, '\0'),
BUILTIN(npcaction, "i???"_s, '\0'),
+ BUILTIN(camera, "???"_s, '\0'),
BUILTIN(announce, "si"_s, '\0'),
BUILTIN(mapannounce, "Msi"_s, '\0'),
BUILTIN(getusers, "i"_s, 'i'),
@@ -3486,8 +4690,8 @@ BuiltinFunction builtin_functions[] =
BUILTIN(enablenpc, "s"_s, '\0'),
BUILTIN(disablenpc, "s"_s, '\0'),
BUILTIN(sc_start, "iTi?"_s, '\0'),
- BUILTIN(sc_end, "i"_s, '\0'),
- BUILTIN(sc_check, "i"_s, 'i'),
+ BUILTIN(sc_end, "i?"_s, '\0'),
+ BUILTIN(sc_check, "i?"_s, 'i'),
BUILTIN(debugmes, "s"_s, '\0'),
BUILTIN(wgm, "s"_s, '\0'),
BUILTIN(gmlog, "s"_s, '\0'),
@@ -3501,14 +4705,13 @@ BuiltinFunction builtin_functions[] =
BUILTIN(pvpon, "M"_s, '\0'),
BUILTIN(pvpoff, "M"_s, '\0'),
BUILTIN(setpvpchannel, "i"_s, '\0'),
- BUILTIN(getpvpflag, "i"_s, 'i'),
+ BUILTIN(getpvpflag, "i?"_s, 'i'),
BUILTIN(emotion, "i?"_s, '\0'),
BUILTIN(mapwarp, "MMxy"_s, '\0'),
BUILTIN(mobcount, "ME"_s, 'i'),
BUILTIN(marriage, "P"_s, 'i'),
BUILTIN(divorce, ""_s, 'i'),
BUILTIN(getitemlink, "I"_s, 's'),
- BUILTIN(getspellinvocation, "s"_s, 's'),
BUILTIN(getpartnerid2, ""_s, 'i'),
BUILTIN(explode, "Nss"_s, '\0'),
BUILTIN(getinventorylist, ""_s, '\0'),
@@ -3525,6 +4728,7 @@ BuiltinFunction builtin_functions[] =
BUILTIN(npcareawarp, "xyxyis"_s, '\0'),
BUILTIN(message, "Ps"_s, '\0'),
BUILTIN(npctalk, "ss?"_s, '\0'),
+ BUILTIN(registercmd, "ss"_s, '\0'),
BUILTIN(title, "s"_s, '\0'),
BUILTIN(smsg, "e??"_s, '\0'),
BUILTIN(remotecmd, "s?"_s, '\0'),
@@ -3533,14 +4737,19 @@ BuiltinFunction builtin_functions[] =
BUILTIN(getmask, ""_s, 'i'),
BUILTIN(getlook, "i"_s, 'i'),
BUILTIN(getsavepoint, "i"_s, '.'),
- BUILTIN(areatimer, "MxyxytE"_s, '\0'),
+ BUILTIN(areatimer, "MxyxytEi"_s, '\0'),
+ BUILTIN(foreach, "iMxyxyE"_s, '\0'),
BUILTIN(isin, "Mxyxy"_s, 'i'),
BUILTIN(iscollision, "Mxy"_s, 'i'),
BUILTIN(shop, "s"_s, '\0'),
BUILTIN(isdead, ""_s, 'i'),
+ BUILTIN(aggravate, "Mxyxyi"_s, '\0'),
BUILTIN(fakenpcname, "ssi"_s, '\0'),
+ BUILTIN(puppet, "mxysi??"_s, 'i'),
+ BUILTIN(destroy, "?"_s, '\0'),
BUILTIN(getx, ""_s, 'i'),
BUILTIN(gety, ""_s, 'i'),
+ BUILTIN(getdir, ""_s, 'i'),
BUILTIN(getnpcx, "?"_s, 'i'),
BUILTIN(getnpcy, "?"_s, 'i'),
BUILTIN(strnpcinfo, "i?"_s, 's'),
@@ -3549,11 +4758,15 @@ BuiltinFunction builtin_functions[] =
BUILTIN(freeloop, "i"_s, '\0'),
BUILTIN(if_then_else, "iii"_s, '.'),
BUILTIN(max, "e?*"_s, 'i'),
- BUILTIN(min, "ii*"_s, 'i'),
+ BUILTIN(min, "e?*"_s, 'i'),
BUILTIN(average, "ii*"_s, 'i'),
BUILTIN(sqrt, "i"_s, 'i'),
BUILTIN(cbrt, "i"_s, 'i'),
BUILTIN(pow, "ii"_s, 'i'),
+ BUILTIN(target, "iii"_s, 'i'),
+ BUILTIN(distance, "ii?"_s, 'i'),
+ BUILTIN(chr, "i"_s, 'i'),
+ BUILTIN(ord, "s"_s, 'i'),
{nullptr, ""_s, ""_s, '\0'},
};
} // namespace map