diff options
author | mekolat <mekolat@users.noreply.github.com> | 2016-04-19 09:41:31 -0400 |
---|---|---|
committer | mekolat <mekolat@users.noreply.github.com> | 2016-04-19 09:41:31 -0400 |
commit | 1ba24673e7064e39406e6faf11d790c2dcc2ac00 (patch) | |
tree | 7e2645f6fdec1dcc63ae56366371246f62865dcd /src/map/script-fun.cpp | |
parent | c3e06ffe6437d27a2a7c6ddb2dc487ff2f007adf (diff) | |
parent | c786a93e91adaf68780a5fd7585f51d0528f92ed (diff) | |
download | tmwa-1ba24673e7064e39406e6faf11d790c2dcc2ac00.tar.gz tmwa-1ba24673e7064e39406e6faf11d790c2dcc2ac00.tar.bz2 tmwa-1ba24673e7064e39406e6faf11d790c2dcc2ac00.tar.xz tmwa-1ba24673e7064e39406e6faf11d790c2dcc2ac00.zip |
Merge self-fork from mekolat/magic-v3
Magic v3
Diffstat (limited to 'src/map/script-fun.cpp')
-rw-r--r-- | src/map/script-fun.cpp | 1399 |
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 |