summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
Diffstat (limited to 'src/map')
-rw-r--r--src/map/map.hpp4
-rw-r--r--src/map/npc-internal.hpp1
-rw-r--r--src/map/npc-parse.cpp2
-rw-r--r--src/map/npc-parse.hpp1
-rw-r--r--src/map/npc.cpp37
-rw-r--r--src/map/script-call.cpp22
-rw-r--r--src/map/script-fun.cpp131
7 files changed, 183 insertions, 15 deletions
diff --git a/src/map/map.hpp b/src/map/map.hpp
index 66253ca..a2d0f5d 100644
--- a/src/map/map.hpp
+++ b/src/map/map.hpp
@@ -331,6 +331,7 @@ struct npc_data : block_list
Opt3 opt3;
Opt0 option;
short flag;
+ bool disposable;
std::list<RString> eventqueuel;
Array<Timer, MAX_EVENTTIMER> eventtimer;
@@ -359,6 +360,9 @@ public:
short xs, ys;
bool event_needs_map;
+ // the npc containing the actual script
+ BlockId parent;
+
// Whether the timer advances if not beyond end.
bool timer_active;
// Tick counter through the timers.
diff --git a/src/map/npc-internal.hpp b/src/map/npc-internal.hpp
index 993263f..8e9e030 100644
--- a/src/map/npc-internal.hpp
+++ b/src/map/npc-internal.hpp
@@ -31,6 +31,7 @@ namespace map
struct event_data
{
dumb_ptr<npc_data_script> nd;
+ BlockId child;
int pos;
};
} // namespace map
diff --git a/src/map/npc-parse.cpp b/src/map/npc-parse.cpp
index 0bd23a9..edfe4c9 100644
--- a/src/map/npc-parse.cpp
+++ b/src/map/npc-parse.cpp
@@ -92,7 +92,6 @@ void npc_delsrcfile(XString name)
}
}
-static
void register_npc_name(dumb_ptr<npc_data> nd)
{
earray<LString, NpcSubtype, NpcSubtype::COUNT> types //=
@@ -474,6 +473,7 @@ bool npc_load_script_none(ast::script::ScriptBody& body, ast::npc::ScriptNone& s
nd->bl_type = BL::NPC;
nd->npc_subtype = NpcSubtype::SCRIPT;
+ id_db.put(nd->bl_id, nd); // fix to get the oid in OnInit
register_npc_name(nd);
for (auto& pair : scriptlabel_db)
diff --git a/src/map/npc-parse.hpp b/src/map/npc-parse.hpp
index 9bc3448..d6719ee 100644
--- a/src/map/npc-parse.hpp
+++ b/src/map/npc-parse.hpp
@@ -39,6 +39,7 @@ dumb_ptr<npc_data> npc_spawn_text(Borrowed<map_local> m, int x, int y,
void npc_addsrcfile(AString name);
void npc_delsrcfile(XString name);
+void register_npc_name(dumb_ptr<npc_data> nd);
bool do_init_npc(void);
} // namespace map
} // namespace tmwa
diff --git a/src/map/npc.cpp b/src/map/npc.cpp
index 5509b6b..ae126ea 100644
--- a/src/map/npc.cpp
+++ b/src/map/npc.cpp
@@ -61,6 +61,15 @@ namespace tmwa
{
namespace map
{
+const std::vector<ByteCode> fake_buffer;
+const ScriptBuffer& fake_script = reinterpret_cast<const ScriptBuffer&>(fake_buffer);
+
+static
+Borrowed<const ScriptBuffer> script_or_parent(dumb_ptr<npc_data_script> nd)
+{
+ return borrow(nd->scr.parent ? fake_script : *nd->scr.script);
+}
+
BlockId npc_get_new_npc_id(void)
{
BlockId rv = npc_id;
@@ -224,7 +233,7 @@ int magic_message(dumb_ptr<map_session_data> caster, XString source_invocation)
if (spell_event.label)
caster->npc_pos = npc_event_do_l(spell_event, caster->bl_id, arg);
else
- caster->npc_pos = run_script_l(ScriptPointer(borrow(*nd->is_script()->scr.script), 0), caster->bl_id, nd->bl_id, arg);
+ caster->npc_pos = run_script_l(ScriptPointer(script_or_parent(nd->is_script()), 0), caster->bl_id, nd->bl_id, arg);
return 1;
}
return 0;
@@ -281,7 +290,9 @@ void npc_event_doall_sub(NpcEvent key, struct event_data *ev,
if (name == p)
{
- run_script_l(ScriptPointer(borrow(*ev->nd->scr.script), ev->pos), rid, ev->nd->bl_id,
+ if (ev->nd->disposable)
+ return; // temporary npcs only respond to commands directly issued to them
+ run_script_l(ScriptPointer(script_or_parent(ev->nd), ev->pos), rid, ev->nd->bl_id,
argv);
(*c)++;
}
@@ -304,7 +315,7 @@ void npc_event_do_sub(NpcEvent key, struct event_data *ev,
if (name == key)
{
- run_script_l(ScriptPointer(borrow(*ev->nd->scr.script), ev->pos), rid, ev->nd->bl_id,
+ run_script_l(ScriptPointer(script_or_parent(ev->nd), ev->pos), rid, ev->nd->bl_id,
argv);
(*c)++;
}
@@ -414,7 +425,7 @@ void npc_eventtimer(TimerData *, tick_t, BlockId id, NpcEvent data)
return;
}
- run_script(ScriptPointer(borrow(*nd->scr.script), ev->pos), id, nd->bl_id);
+ run_script(ScriptPointer(script_or_parent(nd), ev->pos), id, nd->bl_id);
}
/*==========================================
@@ -486,7 +497,7 @@ void npc_timerevent(TimerData *, tick_t tick, BlockId id, interval_t data)
id, next));
}
- run_script(ScriptPointer(borrow(*nd->scr.script), te->pos), BlockId(), nd->bl_id);
+ run_script(ScriptPointer(script_or_parent(nd), te->pos), BlockId(), nd->bl_id);
}
/// Start (or resume) counting ticks to the next npc_timerevent.
@@ -651,7 +662,7 @@ int npc_event(dumb_ptr<map_session_data> sd, NpcEvent eventname,
sd->npc_id = nd->bl_id;
sd->npc_pos =
- run_script(ScriptPointer(borrow(*nd->scr.script), ev->pos), sd->bl_id, nd->bl_id);
+ run_script(ScriptPointer(script_or_parent(nd), ev->pos), sd->bl_id, nd->bl_id);
return 0;
}
@@ -805,7 +816,7 @@ int npc_click(dumb_ptr<map_session_data> sd, BlockId id)
npc_event_dequeue(sd);
break;
case NpcSubtype::SCRIPT:
- sd->npc_pos = run_script(ScriptPointer(borrow(*nd->is_script()->scr.script), 0), sd->bl_id, id);
+ sd->npc_pos = run_script(ScriptPointer(script_or_parent(nd->is_script()), 0), sd->bl_id, id);
break;
case NpcSubtype::MESSAGE:
if (nd->is_message()->message)
@@ -846,7 +857,7 @@ int npc_scriptcont(dumb_ptr<map_session_data> sd, BlockId id)
return 0;
}
- sd->npc_pos = run_script(ScriptPointer(borrow(*nd->is_script()->scr.script), sd->npc_pos), sd->bl_id, id);
+ sd->npc_pos = run_script(ScriptPointer(script_or_parent(nd->is_script()), sd->npc_pos), sd->bl_id, id);
return 0;
}
@@ -1032,18 +1043,18 @@ void npc_free_internal(dumb_ptr<npc_data> nd_)
if (nd_->npc_subtype == NpcSubtype::SCRIPT)
{
dumb_ptr<npc_data_script> nd = nd_->is_script();
+ nd->scr.timerid.cancel();
nd->scr.timer_eventv.clear();
-
- {
- nd->scr.script.reset();
- nd->scr.label_listv.clear();
- }
+ nd->scr.script.reset();
+ nd->scr.label_listv.clear();
}
else if (nd_->npc_subtype == NpcSubtype::MESSAGE)
{
dumb_ptr<npc_data_message> nd = nd_->is_message();
nd->message = AString();
}
+ if (nd_->name)
+ npcs_by_name.put(nd_->name, nullptr);
nd_.delete_();
}
diff --git a/src/map/script-call.cpp b/src/map/script-call.cpp
index 54f6c01..b8f531a 100644
--- a/src/map/script-call.cpp
+++ b/src/map/script-call.cpp
@@ -876,7 +876,26 @@ int run_script_l(ScriptPointer sp, BlockId rid, BlockId oid,
{
struct script_stack stack;
ScriptState st;
- dumb_ptr<map_session_data> sd = map_id2sd(rid);
+ dumb_ptr<block_list> bl = map_id2bl(rid);
+ dumb_ptr<map_session_data> sd = bl? bl->is_player(): nullptr;
+ if (oid)
+ {
+ dumb_ptr<block_list> oid_bl = map_id2bl(oid);
+ if (oid_bl->bl_type == BL::NPC)
+ {
+ dumb_ptr<npc_data> nd = oid_bl->is_npc();
+ if(nd->npc_subtype == NpcSubtype::SCRIPT)
+ {
+ dumb_ptr<npc_data_script> nds = nd->is_script();
+ if (nds->scr.parent)
+ {
+ dumb_ptr<npc_data_script> parent = map_id2bl(nds->scr.parent)->is_npc()->is_script();
+ assert(parent->bl_type == BL::NPC && parent->npc_subtype == NpcSubtype::SCRIPT);
+ sp = ScriptPointer(borrow(*parent->scr.script), sp.pos);
+ }
+ }
+ }
+ }
P<const ScriptBuffer> rootscript = TRY_UNWRAP(sp.code, return -1);
int i;
if (sp.pos >> 24)
@@ -892,6 +911,7 @@ int run_script_l(ScriptPointer sp, BlockId rid, BlockId oid,
st.scriptp = sp;
st.rid = rid;
st.oid = oid;
+
for (i = 0; i < args.size(); i++)
{
if (args[i].name.back() == '$')
diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp
index 8044b4d..4ef6499 100644
--- a/src/map/script-fun.cpp
+++ b/src/map/script-fun.cpp
@@ -50,6 +50,7 @@
#include "map.hpp"
#include "mob.hpp"
#include "npc.hpp"
+#include "npc-parse.hpp"
#include "party.hpp"
#include "pc.hpp"
#include "script-call-internal.hpp"
@@ -57,6 +58,7 @@
#include "script-persist.hpp"
#include "skill.hpp"
#include "storage.hpp"
+#include "npc-internal.hpp"
#include "../poison.hpp"
@@ -773,6 +775,132 @@ void builtin_foreach(ScriptState *st)
x1, y1,
block_type);
}
+/*========================================
+ * Destructs a temp NPC
+ *----------------------------------------
+ */
+static
+void builtin_destroypuppet(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);
+ 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;
+ nd->disposable = true; // allow to destroy
+
+ // PlayerName::SpellName
+ NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARG(3))));
+ PRINTF("Npc: %s\n"_fmt, npc);
+ nd->name = npc;
+
+ // Dynamically set location
+ nd->bl_m = m;
+ nd->bl_x = x;
+ nd->bl_y = y;
+ 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));
+}
/*==========================================
* 変数設定
@@ -2472,6 +2600,7 @@ void builtin_attachrid(ScriptState *st)
push_int<ScriptDataInt>(st->stack, (map_id2sd(st->rid) != nullptr));
}
+
/*==========================================
* RIDのデタッチ
*------------------------------------------
@@ -3838,6 +3967,8 @@ BuiltinFunction builtin_functions[] =
BUILTIN(isdead, ""_s, 'i'),
BUILTIN(aggravate, "Mxyxyi"_s, '\0'),
BUILTIN(fakenpcname, "ssi"_s, '\0'),
+ BUILTIN(puppet, "mxysi"_s, 'i'),
+ BUILTIN(destroypuppet, "?"_s, '\0'),
BUILTIN(getx, ""_s, 'i'),
BUILTIN(gety, ""_s, 'i'),
BUILTIN(getnpcx, "?"_s, 'i'),