From ef4ae4d281127a0b84a73ab034bd22d6281a09ed Mon Sep 17 00:00:00 2001 From: mekolat Date: Thu, 18 Jun 2015 01:56:31 -0500 Subject: implement puppet npcs --- src/map/map.hpp | 4 ++ src/map/npc-internal.hpp | 1 + src/map/npc-parse.cpp | 2 +- src/map/npc-parse.hpp | 1 + src/map/npc.cpp | 37 ++++++++----- src/map/script-call.cpp | 22 +++++++- src/map/script-fun.cpp | 131 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 183 insertions(+), 15 deletions(-) (limited to 'src') 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 eventqueuel; Array 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 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 nd) { earray 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_spawn_text(Borrowed m, int x, int y, void npc_addsrcfile(AString name); void npc_delsrcfile(XString name); +void register_npc_name(dumb_ptr 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 fake_buffer; +const ScriptBuffer& fake_script = reinterpret_cast(fake_buffer); + +static +Borrowed script_or_parent(dumb_ptr 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 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 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 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 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 nd_) if (nd_->npc_subtype == NpcSubtype::SCRIPT) { dumb_ptr 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 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 sd = map_id2sd(rid); + dumb_ptr bl = map_id2bl(rid); + dumb_ptr sd = bl? bl->is_player(): nullptr; + if (oid) + { + dumb_ptr oid_bl = map_id2bl(oid); + if (oid_bl->bl_type == BL::NPC) + { + dumb_ptr nd = oid_bl->is_npc(); + if(nd->npc_subtype == NpcSubtype::SCRIPT) + { + dumb_ptr nds = nd->is_script(); + if (nds->scr.parent) + { + dumb_ptr 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 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(conv_num(st, &AARG(0))); + else + id = st->oid; + + dumb_ptr 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 bl = map_id2bl(st->oid); + dumb_ptr parent_nd = bl->is_npc()->is_script(); + dumb_ptr nd; + nd.new_(); + + MapName mapname = stringish(ZString(conv_str(st, &AARG(0)))); + x = conv_num(st, &AARG(1)); + y = conv_num(st, &AARG(2)); + Species sprite = wrap(static_cast(conv_num(st, &AARG(4)))); + + P 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(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(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(st->stack, unwrap(nd->bl_id)); +} /*========================================== * 変数設定 @@ -2472,6 +2600,7 @@ void builtin_attachrid(ScriptState *st) push_int(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'), -- cgit v1.2.3-60-g2f50