From 1853e964e96c41e762ca0ab97259ee4e79d86ec7 Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Mon, 3 Nov 2014 13:35:54 -0800 Subject: Use the new ASTs --- src/map/atcommand.cpp | 20 +- src/map/fwd.hpp | 1 + src/map/itemdb.cpp | 132 +++---- src/map/magic-v2.cpp | 17 +- src/map/map.cpp | 4 +- src/map/npc-parse.cpp | 977 +++++++++++++++++++++++++---------------------- src/map/npc-parse.hpp | 2 +- src/map/script-parse.cpp | 6 +- src/map/script-parse.hpp | 2 +- 9 files changed, 602 insertions(+), 559 deletions(-) (limited to 'src/map') diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index d3e215f..0b23ddd 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -57,6 +57,8 @@ #include "../high/mmo.hpp" #include "../high/utils.hpp" +#include "../ast/npc.hpp" + #include "battle.hpp" #include "chrif.hpp" #include "clif.hpp" @@ -4136,13 +4138,21 @@ ATCE atcommand_addwarp(Session *s, dumb_ptr sd, if (!extract(message, record<' '>(&mapname, &x, &y))) return ATCE::USAGE; - AString w1 = STRPRINTF("%s,%d,%d"_fmt, sd->mapname_, sd->bl_x, sd->bl_y); AString w3 = STRPRINTF("%s%d%d%d%d"_fmt, mapname, sd->bl_x, sd->bl_y, x, y); - AString w4 = STRPRINTF("1,1,%s.gat,%d,%d"_fmt, mapname, x, y); - NpcName w3name = stringish(w3); - int ret = npc_parse_warp(w1, "warp"_s, w3name, w4); - if (ret) + + ast::npc::Warp warp; + warp.m.data = sd->mapname_; + warp.x.data = sd->bl_x; + warp.y.data = sd->bl_y; + warp.name.data = w3name; + warp.xs.data = 1+2; + warp.ys.data = 1+2; + warp.to_m.data = mapname; + warp.to_x.data = x; + warp.to_y.data = y; + + if (!npc_load_warp(warp)) // warp failed return ATCE::RANGE; diff --git a/src/map/fwd.hpp b/src/map/fwd.hpp index f998b77..3e4f221 100644 --- a/src/map/fwd.hpp +++ b/src/map/fwd.hpp @@ -34,6 +34,7 @@ #include "../proto2/fwd.hpp" // rank 8 #include "../high/fwd.hpp" // rank 9 #include "../wire/fwd.hpp" // rank 9 +#include "../ast/fwd.hpp" // rank 10 // map/fwd.hpp is rank ∞ because it is an executable diff --git a/src/map/itemdb.cpp b/src/map/itemdb.cpp index 2e3d0c5..2ce03ea 100644 --- a/src/map/itemdb.cpp +++ b/src/map/itemdb.cpp @@ -30,11 +30,13 @@ #include "../io/cxxstdio.hpp" #include "../io/extract.hpp" -#include "../io/read.hpp" +#include "../io/line.hpp" #include "../mmo/config_parse.hpp" #include "../mmo/extract_enums.hpp" +#include "../ast/item.hpp" + #include "script-parse.hpp" #include "../poison.hpp" @@ -149,99 +151,63 @@ int itemdb_isequip3(ItemNameId nameid) bool itemdb_readdb(ZString filename) { - bool rv = true; - - int ln = 0, lines = 0; + io::LineCharReader in(filename); + if (!in.is_open()) { - io::ReadFile in(filename); - - if (!in.is_open()) - { - PRINTF("can't read %s\n"_fmt, filename); - return false; - } + PRINTF("can't read %s\n"_fmt, filename); + return false; + } - lines = 0; + int ln = 0; - AString line; - while (in.getline(line)) + while (true) + { + auto res = TRY_UNWRAP(ast::item::parse_item(in), + { + PRINTF("read %s done (count=%d)\n"_fmt, filename, ln); + return true; + }); + if (res.get_failure()) + PRINTF("%s\n"_fmt, res.get_failure()); + ast::item::ItemOrComment ioc = TRY_UNWRAP(std::move(res.get_success()), return false); + + MATCH (ioc) { - lines++; - if (is_comment(line)) - continue; - // a line is 17 normal fields followed by 2 {} fields - // the fields are separated by ", *", but there may be , - // in the {}. - - auto it = std::find(line.begin(), line.end(), '{'); - XString main_part = line.xislice_h(it).rstrip(); - // According to the code, tail_part may be empty. See later. - ZString tail_part = line.xislice_t(it); - - XString unused_slot_count; - item_data idv {}; - if (!extract( - main_part, record<','>( - &idv.nameid, - lstripping(&idv.name), - lstripping(&idv.jname), - lstripping(&idv.type), - lstripping(&idv.value_buy), - lstripping(&idv.value_sell), - lstripping(&idv.weight), - lstripping(&idv.atk), - lstripping(&idv.def), - lstripping(&idv.range), - lstripping(&idv.magic_bonus), - lstripping(&unused_slot_count), - lstripping(&idv.sex), - lstripping(&idv.equip), - lstripping(&idv.wlv), - lstripping(&idv.elv), - lstripping(&idv.look) - ) - ) - ) + CASE(const ast::item::Comment&, c) { - PRINTF("%s:%d: error: bad item line: %s\n"_fmt, - filename, lines, line); - rv = false; - continue; + (void)c; } - - ln++; - - Borrowed id = itemdb_search(idv.nameid); - *id = std::move(idv); - if (id->value_buy == 0 && id->value_sell == 0) + CASE(const ast::item::Item&, item) { + ln++; + + item_data idv {}; + idv.nameid = item.id.data; + idv.name = item.name.data; + idv.jname = item.jname.data; + idv.type = item.type.data; + idv.value_buy = item.buy_price.data ?: item.sell_price.data * 2; + idv.value_sell = item.sell_price.data ?: item.buy_price.data / 2; + idv.weight = item.weight.data; + idv.atk = item.atk.data; + idv.def = item.def.data; + idv.range = item.range.data; + idv.magic_bonus = item.magic_bonus.data; + idv.sex = item.gender.data; + idv.equip = item.loc.data; + idv.wlv = item.wlv.data; + idv.elv = item.elv.data; + idv.look = item.view.data; + + idv.use_script = compile_script(item.use_script, true); + idv.equip_script = compile_script(item.equip_script, true); + + Borrowed id = itemdb_search(idv.nameid); + *id = std::move(idv); } - else if (id->value_buy == 0) - { - id->value_buy = id->value_sell * 2; - } - else if (id->value_sell == 0) - { - id->value_sell = id->value_buy / 2; - } - - id->use_script = nullptr; - id->equip_script = nullptr; - - if (!tail_part) - continue; - id->use_script = parse_script(tail_part, lines, true); - - tail_part = tail_part.xislice_t(std::find(tail_part.begin() + 1, tail_part.end(), '{')); - if (!tail_part) - continue; - id->equip_script = parse_script(tail_part, lines, true); } - PRINTF("read %s done (count=%d)\n"_fmt, filename, ln); } - - return rv; } /*========================================== diff --git a/src/map/magic-v2.cpp b/src/map/magic-v2.cpp index eeeb989..26c1a19 100644 --- a/src/map/magic-v2.cpp +++ b/src/map/magic-v2.cpp @@ -36,6 +36,8 @@ #include "../sexpr/parser.hpp" +#include "../ast/script.hpp" + #include "itemdb.hpp" #include "magic-expr.hpp" #include "magic-interpreter.hpp" @@ -785,7 +787,20 @@ namespace magic_v2 if (s._list[1]._type != sexpr::STRING) return fail(s._list[1], "not string"_s); ZString body = s._list[1]._str; - std::unique_ptr script = parse_script(body, s._list[1]._span.begin.line, true); + auto begin = s._list[1]._span.begin; + io::LineCharReader lr(io::from_string, begin.filename, body, begin.line, begin.column); + ast::script::ScriptOptions opt; + opt.implicit_start = true; + opt.implicit_end = true; + opt.no_event = true; + auto code_res = ast::script::parse_script_body(lr, opt); + if (code_res.get_failure()) + { + PRINTF("%s\n"_fmt, code_res.get_failure()); + } + auto code = TRY_UNWRAP(code_res.get_success(), + return fail(s._list[1], "script does not compile"_s)); + std::unique_ptr script = compile_script(code, true); if (!script) return fail(s._list[1], "script does not compile"_s); EffectScript e; diff --git a/src/map/map.cpp b/src/map/map.cpp index 9ae865f..b5f08ea 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -1295,7 +1295,7 @@ bool map_readmap(map_local *m, size_t num, MapName fn) int xs = m->xs = gat_v[0] | gat_v[1] << 8; int ys = m->ys = gat_v[2] | gat_v[3] << 8; - PRINTF("\rLoading Maps [%zu/%zu]: %-30s (%i, %i)"_fmt, + PRINTF("Loading Maps [%zu/%zu]: %-30s (%i, %i)\r"_fmt, num, maps_db.size(), fn, xs, ys); fflush(stdout); @@ -1348,7 +1348,7 @@ bool map_readallmap(void) } } - PRINTF("\rMaps Loaded: %-65zu\n"_fmt, maps_db.size()); + PRINTF("Maps Loaded: %-65zu\n"_fmt, maps_db.size()); if (maps_removed) { PRINTF("Cowardly refusing to keep going after removing %d maps.\n"_fmt, diff --git a/src/map/npc-parse.cpp b/src/map/npc-parse.cpp index 81a5ba2..390f551 100644 --- a/src/map/npc-parse.cpp +++ b/src/map/npc-parse.cpp @@ -32,12 +32,14 @@ #include "../io/cxxstdio.hpp" #include "../io/extract.hpp" -#include "../io/read.hpp" +#include "../io/line.hpp" #include "../mmo/config_parse.hpp" #include "../high/extract_mmo.hpp" +#include "../ast/npc.hpp" + #include "battle.hpp" #include "clif.hpp" #include "itemdb.hpp" @@ -57,24 +59,12 @@ std::list npc_srcs; static int npc_warp, npc_shop, npc_script, npc_mob; -// -// 初期化関係 -// - -/*========================================== - * 読み込むnpcファイルのクリア - *------------------------------------------ - */ static void npc_clearsrcfile(void) { npc_srcs.clear(); } -/*========================================== - * 読み込むnpcファイルの追加 - *------------------------------------------ - */ void npc_addsrcfile(AString name) { if (name == "clear"_s) @@ -86,10 +76,6 @@ void npc_addsrcfile(AString name) npc_srcs.push_back(name); } -/*========================================== - * 読み込むnpcファイルの削除 - *------------------------------------------ - */ void npc_delsrcfile(XString name) { if (name == "all"_s) @@ -145,26 +131,19 @@ void register_npc_name(dumb_ptr nd) npcs_by_name.put(nd->name, nd); } -/*========================================== - * warp行解析 - *------------------------------------------ - */ -int npc_parse_warp(XString w1, XString, NpcName w3, XString w4) +// extern for atcommand @addwarp +bool npc_load_warp(ast::npc::Warp& warp) { - int x, y, xs, ys, to_x, to_y; - int i, j; - MapName mapname, to_mapname; - dumb_ptr nd; + MapName mapname = warp.m.data; + int x = warp.x.data, y = warp.y.data; - if (!extract(w1, record<','>(&mapname, &x, &y)) || - !extract(w4, record<','>(&xs, &ys, &to_mapname, &to_x, &to_y))) - { - PRINTF("bad warp line : %s\n"_fmt, w3); - return 1; - } + int xs = warp.xs.data, ys = warp.ys.data; + MapName to_mapname = warp.to_m.data; + int to_x = warp.to_x.data, to_y = warp.to_y.data; - P m = TRY_UNWRAP(map_mapname2mapid(mapname), return 1); + P m = TRY_UNWRAP(map_mapname2mapid(mapname), abort()); + dumb_ptr nd; nd.new_(); nd->bl_id = npc_get_new_npc_id(); nd->n = map_addnpc(m, nd); @@ -175,7 +154,7 @@ int npc_parse_warp(XString w1, XString, NpcName w3, XString w4) nd->bl_y = y; nd->dir = DIR::S; nd->flag = 0; - nd->name = w3; + nd->name = warp.name.data; if (!battle_config.warp_point_debug) nd->npc_class = WARP_CLASS; @@ -187,16 +166,14 @@ int npc_parse_warp(XString w1, XString, NpcName w3, XString w4) nd->opt2 = Opt2::ZERO; nd->opt3 = Opt3::ZERO; nd->warp.name = to_mapname; - xs += 2; - ys += 2; nd->warp.x = to_x; nd->warp.y = to_y; nd->warp.xs = xs; nd->warp.ys = ys; - for (i = 0; i < ys; i++) + for (int i = 0; i < ys; i++) { - for (j = 0; j < xs; j++) + for (int j = 0; j < xs; j++) { int x_lo = x - xs / 2; int y_lo = y - ys / 2; @@ -216,74 +193,42 @@ int npc_parse_warp(XString w1, XString, NpcName w3, XString w4) clif_spawnnpc(nd); register_npc_name(nd); - return 0; -} - -static -bool extract(XString xs, npc_item_list *itv) -{ - XString name_or_id; - if (!extract(xs, record<':'>(&name_or_id, &itv->value))) - return false; - - P id = ((extract(name_or_id, &itv->nameid) && itv->nameid) - ? ({ - P id_ = itemdb_search(itv->nameid); - id_; - }) - : ({ - P id_ = TRY_UNWRAP(itemdb_searchname(name_or_id.rstrip()), return false); - itv->nameid = id_->nameid; - id_; - })); - - if (itv->value < 0) - { - itv->value = id->value_buy * abs(itv->value); - } return true; } -/*========================================== - * shop行解析 - *------------------------------------------ - */ static -int npc_parse_shop(XString w1, XString, NpcName w3, ZString w4a) +bool npc_load_shop(ast::npc::Shop& shop) { - int x, y; - DIR dir; - MapName mapname; + MapName mapname = shop.m.data; + int x = shop.x.data, y = shop.y.data; + DIR dir = shop.d.data; dumb_ptr nd; - ZString::iterator w4comma; - Species npc_class; - - int dir_; // TODO use enum directly in extract - if (!extract(w1, record<','>(&mapname, &x, &y, &dir_)) - || dir_ < 0 || dir_ >= 8 - || (w4comma = std::find(w4a.begin(), w4a.end(), ',')) == w4a.end() - || !extract(w4a.xislice_h(w4comma), &npc_class)) - { - PRINTF("bad shop line : %s\n"_fmt, w3); - return 1; - } - dir = static_cast(dir_); - P m = TRY_UNWRAP(map_mapname2mapid(mapname), return 1); + Species npc_class = shop.npc_class.data; - nd.new_(); - ZString w4b = w4a.xislice_t(w4comma + 1); - - if (!extract(w4b, vrec<','>(&nd->shop_items))) - { - PRINTF("bad shop items : %s\n"_fmt, w3); - PRINTF(" somewhere --> %s\n"_fmt, w4b); - nd->shop_items.clear(); - } + P m = TRY_UNWRAP(map_mapname2mapid(mapname), abort()); - if (nd->shop_items.empty()) - { - nd.delete_(); - return 1; + nd.new_(); + nd->shop_items.reserve(shop.items.data.size()); + for (auto& it : shop.items.data) + { + nd->shop_items.emplace_back(); + auto& back = nd->shop_items.back(); + P id = ((extract(it.data.name.data, &back.nameid) && back.nameid) + ? ({ + P id_ = TRY_UNWRAP(itemdb_exists(back.nameid), { it.data.name.span.error("No item with this numerical id"_s); return false; }); + id_; + }) + : ({ + P id_ = TRY_UNWRAP(itemdb_searchname(XString(it.data.name.data)), { it.data.name.span.error("No item with this name"_s); return false; }); + back.nameid = id_->nameid; + id_; + })); + + if (it.data.value_multiply) + { + back.value = id->value_buy * back.value; + } + return true; } nd->bl_prev = nd->bl_next = nullptr; @@ -293,7 +238,7 @@ int npc_parse_shop(XString w1, XString, NpcName w3, ZString w4a) nd->bl_id = npc_get_new_npc_id(); nd->dir = dir; nd->flag = 0; - nd->name = w3; + nd->name = shop.name.data; nd->npc_class = npc_class; nd->speed = 200_ms; nd->option = Opt0::ZERO; @@ -309,164 +254,289 @@ int npc_parse_shop(XString w1, XString, NpcName w3, ZString w4a) clif_spawnnpc(nd); register_npc_name(nd); - return 0; + return true; } -/*========================================== - * NPCのラベルデータコンバート - *------------------------------------------ - */ static -void npc_convertlabel_db(ScriptLabel lname, int pos, dumb_ptr nd) +bool npc_load_monster(ast::npc::Monster& monster) { - nullpo_retv(nd); + MapName mapname = monster.m.data; + int x = monster.x.data, y = monster.y.data; + int xs = monster.xs.data, ys = monster.ys.data; - struct npc_label_list eln {}; - eln.name = lname; - eln.pos = pos; - nd->scr.label_listv.push_back(std::move(eln)); + Species mob_class = monster.mob_class.data; + int num = monster.num.data; + interval_t delay1 = monster.delay1.data; + interval_t delay2 = monster.delay2.data; + NpcEvent eventname = monster.event.data; + + P m = TRY_UNWRAP(map_mapname2mapid(mapname), abort()); + + if (num > 1 && battle_config.mob_count_rate != 100) + { + num = num * battle_config.mob_count_rate / 100; + if (num < 1) + num = 1; + } + + for (int i = 0; i < num; i++) + { + dumb_ptr md; + md.new_(); + + md->bl_prev = nullptr; + md->bl_next = nullptr; + md->bl_m = m; + md->bl_x = x; + md->bl_y = y; + MobName expected = get_mob_db(mob_class).jname; + if (monster.name.data != expected) + { + monster.name.span.warning(STRPRINTF("Visible label/jname should match: %s"_fmt, expected)); + } + if (monster.name.data == ENGLISH_NAME) + md->name = get_mob_db(mob_class).name; + else if (monster.name.data == JAPANESE_NAME) + md->name = get_mob_db(mob_class).jname; + else + md->name = monster.name.data; + + md->n = i; + md->mob_class = mob_class; + md->bl_id = npc_get_new_npc_id(); + md->spawn.m = m; + md->spawn.x0 = x; + md->spawn.y0 = y; + md->spawn.xs = xs; + md->spawn.ys = ys; + md->spawn.delay1 = delay1; + md->spawn.delay2 = delay2; + + really_memzero_this(&md->state); + // md->timer = nullptr; + md->target_id = BlockId(); + md->attacked_id = BlockId(); + + md->lootitemv.clear(); + + md->npc_event = eventname; + + md->bl_type = BL::MOB; + map_addiddb(md); + mob_spawn(md->bl_id); + + npc_mob++; + } + + return true; } -/*========================================== - * script行解析 - *------------------------------------------ - */ static -int npc_parse_script(XString w1, XString w2, NpcName w3, ZString w4, - XString first_line, io::ReadFile& fp, int *lines) +bool npc_load_mapflag(ast::npc::MapFlag& mapflag) { - int x, y; - DIR dir = DIR::S; - int xs = 0, ys = 0; // [Valaris] thanks to fov - Species npc_class; - MapName mapname; - std::unique_ptr script = nullptr; - dumb_ptr nd; - int evflag = 0; + MapName mapname = mapflag.m.data; + P m = TRY_UNWRAP(map_mapname2mapid(mapname), abort()); - P m = borrow(undefined_gat); - if (w1 == "-"_s) + MapFlag mf; + if (!extract(mapflag.name.data, &mf)) { - x = 0; - y = 0; - m = borrow(undefined_gat); + mapflag.name.span.error("No such mapflag"_s); + return false; } - else + + if (battle_config.pk_mode && mf == MapFlag::NOPVP) { - int dir_; // TODO use enum directly in extract - if (!extract(w1, record<','>(&mapname, &x, &y, &dir_)) - || dir_ < 0 || dir_ >= 8 - || (w2 == "script"_s && !w4.contains(','))) + if (mapflag.vec_extra.data.size()) { - PRINTF("bad script line : %s\n"_fmt, w3); - return 1; + mapflag.vec_extra.span.error("No extra argument expected for mapflag 'nopvp'"_s); + return false; } - dir = static_cast(dir_); - m = map_mapname2mapid(mapname).copy_or(borrow(undefined_gat)); + m->flag.set(MapFlag::NOPVP, 1); + m->flag.set(MapFlag::PVP, 0); + return true; } - if (w2 == "script"_s) + MapName savemap; + int savex, savey; + + if (mf == MapFlag::NOSAVE) { - // may be empty - MString srcbuf; - srcbuf += first_line.xislice_t(std::find(first_line.begin(), first_line.end(), '{')); - // Note: it was a bug that this was missing. I think. - int startline = *lines; - - // while (!srcbuf.rstrip().endswith('}')) - while (true) + if (mapflag.vec_extra.data.size() == 3 + && extract(mapflag.vec_extra.data[0].data, &savemap) + && extract(mapflag.vec_extra.data[1].data, &savex) + && extract(mapflag.vec_extra.data[2].data, &savey) + && map_mapname2mapid(savemap).is_some()) { - auto it = std::find_if_not(srcbuf.rbegin(), srcbuf.rend(), [](char c){ return c == ' ' || c == '\n'; }); - if (it != srcbuf.rend() && *it == '}') - break; - - AString line; - if (!fp.getline(line)) - // eof - break; - (*lines)++; - if (!srcbuf) - { - // may be a no-op - srcbuf += line.xislice_t(std::find(line.begin(), line.end(), '{')); - // safe to execute more than once - // But will usually only happen once - startline = *lines; - } - else - srcbuf += line; - srcbuf += '\n'; + m->save.map_ = savemap; + m->save.x = savex; + m->save.y = savey; + } + else + { + mapflag.vec_extra.span.error("Unable to extract nosave savepoint"_s); + return false; + } + } + else if (mf == MapFlag::RESAVE) + { + if (mapflag.vec_extra.data.size() == 3 + && extract(mapflag.vec_extra.data[0].data, &savemap) + && extract(mapflag.vec_extra.data[1].data, &savex) + && extract(mapflag.vec_extra.data[2].data, &savey) + && map_mapname2mapid(savemap).is_some()) + { + m->resave.map_ = savemap; + m->resave.x = savex; + m->resave.y = savey; + } + else + { + mapflag.vec_extra.span.error("Unable to extract resave savepoint"_s); + return false; } - script = parse_script(AString(srcbuf), startline, false); - if (script == nullptr) - // script parse error? - return 1; } else { - assert(0 && "duplicate() is no longer supported!\n"_s); - return 0; + if (mapflag.vec_extra.data.size()) + { + mapflag.vec_extra.span.error("No extra argument expected for mapflag"_s); + return false; + } } + m->flag.set(mf, true); + + return true; +} + +static +void npc_convertlabel_db(ScriptLabel lname, int pos, dumb_ptr nd) +{ + nullpo_retv(nd); + + struct npc_label_list eln {}; + eln.name = lname; + eln.pos = pos; + nd->scr.label_listv.push_back(std::move(eln)); +} + +static +bool npc_load_script_function(ast::script::ScriptBody& body, ast::npc::ScriptFunction& script_function) +{ + std::unique_ptr script = compile_script(body, false); + if (script == nullptr) + return false; + + userfunc_db.put(script_function.name.data, std::move(script)); + + return true; +} + +static +bool npc_load_script_none(ast::script::ScriptBody& body, ast::npc::ScriptNone& script_none) +{ + std::unique_ptr script = compile_script(body, false); + if (script == nullptr) + return false; + dumb_ptr nd; nd.new_(); - if (m == borrow(undefined_gat)) - { - } - else if (extract(w4, record<','>(&npc_class, &xs, &ys))) - { - if (xs >= 0) - xs = xs * 2 + 1; - if (ys >= 0) - ys = ys * 2 + 1; + nd->name = script_none.name.data; - if (npc_class != NEGATIVE_SPECIES) - { + nd->bl_prev = nd->bl_next = nullptr; + nd->bl_m = borrow(undefined_gat); + nd->bl_x = 0; + nd->bl_y = 0; + nd->bl_id = npc_get_new_npc_id(); + nd->dir = DIR::S; + nd->flag = 0; + nd->npc_class = NEGATIVE_SPECIES; + nd->speed = 200_ms; + nd->scr.script = std::move(script); + nd->option = Opt0::ZERO; + nd->opt1 = Opt1::ZERO; + nd->opt2 = Opt2::ZERO; + nd->opt3 = Opt3::ZERO; - for (int i = 0; i < ys; i++) - { - for (int j = 0; j < xs; j++) - { - int x_lo = x - xs / 2; - int y_lo = y - ys / 2; - int xc = x_lo + j; - int yc = y_lo + i; - MapCell t = map_getcell(m, xc, yc); - if (bool(t & MapCell::UNWALKABLE)) - continue; - map_setcell(m, xc, yc, t | MapCell::NPC_NEAR); - } - } - } + npc_script++; + nd->bl_type = BL::NPC; + nd->npc_subtype = NpcSubtype::SCRIPT; - nd->scr.xs = xs; - nd->scr.ys = ys; - } - else - { - XString w4x = w4; - if (w4x.endswith(',')) - w4x = w4x.xrslice_h(1); - if (!extract(w4x, &npc_class)) - abort(); - nd->scr.xs = 0; - nd->scr.ys = 0; - } + register_npc_name(nd); - if (npc_class == NEGATIVE_SPECIES && m != borrow(undefined_gat)) - { - evflag = 1; - } + for (auto& pair : scriptlabel_db) + npc_convertlabel_db(pair.first, pair.second, nd); - if (w3.contains(':')) + for (npc_label_list& el : nd->scr.label_listv) { - assert(false && "feature removed"_s); - abort(); + 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 : nd->scr.label_listv) { - nd->name = w3; + 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)); + } } + // The counter starts stopped with 0 ticks, which is the first event, + // unless there is none, in which case begin == end. + nd->scr.timer = interval_t::zero(); + nd->scr.next_event = nd->scr.timer_eventv.begin(); + // nd->scr.timerid = nullptr; + + return true; +} + +static +bool npc_load_script_map_none(ast::script::ScriptBody& body, ast::npc::ScriptMapNone& script_map_none) +{ + MapName mapname = script_map_none.m.data; + int x = script_map_none.x.data, y = script_map_none.y.data; + DIR dir = script_map_none.d.data; + P m = TRY_UNWRAP(map_mapname2mapid(mapname), + { + script_map_none.m.span.error("No such map"_s); + return false; + }); + + std::unique_ptr script = compile_script(body, false); + if (script == nullptr) + return false; + + dumb_ptr nd; + nd.new_(); + + nd->name = script_map_none.name.data; nd->bl_prev = nd->bl_next = nullptr; nd->bl_m = m; @@ -475,7 +545,7 @@ int npc_parse_script(XString w1, XString w2, NpcName w3, ZString w4, nd->bl_id = npc_get_new_npc_id(); nd->dir = dir; nd->flag = 0; - nd->npc_class = npc_class; + nd->npc_class = NEGATIVE_SPECIES; nd->speed = 200_ms; nd->scr.script = std::move(script); nd->option = Opt0::ZERO; @@ -486,24 +556,20 @@ int npc_parse_script(XString w1, XString w2, NpcName w3, ZString w4, npc_script++; nd->bl_type = BL::NPC; nd->npc_subtype = NpcSubtype::SCRIPT; - if (m != borrow(undefined_gat)) - { - nd->n = map_addnpc(m, nd); - map_addblock(nd); - if (evflag) - { - struct event_data ev {}; - ev.nd = nd; - ev.pos = 0; - NpcEvent npcev; - npcev.npc = nd->name; - npcev.label = ScriptLabel(); - ev_db.insert(npcev, ev); - } - else - clif_spawnnpc(nd); + nd->n = map_addnpc(m, nd); + map_addblock(nd); + + { + struct event_data ev {}; + ev.nd = nd; + ev.pos = 0; + NpcEvent npcev; + npcev.npc = nd->name; + npcev.label = ScriptLabel(); + ev_db.insert(npcev, ev); } + register_npc_name(nd); for (auto& pair : scriptlabel_db) @@ -526,8 +592,6 @@ int npc_parse_script(XString w1, XString w2, NpcName w3, ZString w4, } } - //----------------------------------------- - // ラベルデータからタイマーイベント取り込み for (npc_label_list& el : nd->scr.label_listv) { int t_ = 0; @@ -558,185 +622,171 @@ int npc_parse_script(XString w1, XString w2, NpcName w3, ZString w4, nd->scr.next_event = nd->scr.timer_eventv.begin(); // nd->scr.timerid = nullptr; - return 0; + return true; } -/*========================================== - * function行解析 - *------------------------------------------ - */ static -int npc_parse_function(XString, XString, XString w3, ZString, - XString first_line, io::ReadFile& fp, int *lines) +bool npc_load_script_map(ast::script::ScriptBody& body, ast::npc::ScriptMap& script_map) { - MString srcbuf; - srcbuf += first_line.xislice_t(std::find(first_line.begin(), first_line.end(), '{')); - int startline = *lines; + MapName mapname = script_map.m.data; + int x = script_map.x.data, y = script_map.y.data; + DIR dir = script_map.d.data; + + P m = TRY_UNWRAP(map_mapname2mapid(mapname), + { + script_map.m.span.error("No such map"_s); + return false; + }); + + std::unique_ptr script = compile_script(body, false); + if (script == nullptr) + return false; + + + dumb_ptr nd; + nd.new_(); + + Species npc_class = script_map.npc_class.data; + int xs = script_map.xs.data, ys = script_map.ys.data; - while (true) { - auto it = std::find_if_not(srcbuf.rbegin(), srcbuf.rend(), [](char c){ return c == ' ' || c == '\n'; }); - if (it != srcbuf.rend() && *it == '}') - break; - - AString line; - if (!fp.getline(line)) - break; - (*lines)++; - if (!srcbuf) + for (int i = 0; i < ys; i++) { - srcbuf += line.xislice_t(std::find(line.begin(), line.end(), '{')); - startline = *lines; + for (int j = 0; j < xs; j++) + { + int x_lo = x - xs / 2; + int y_lo = y - ys / 2; + int xc = x_lo + j; + int yc = y_lo + i; + MapCell t = map_getcell(m, xc, yc); + if (bool(t & MapCell::UNWALKABLE)) + continue; + map_setcell(m, xc, yc, t | MapCell::NPC_NEAR); + } } - else - srcbuf += line; - srcbuf += '\n'; - } - std::unique_ptr script = parse_script(AString(srcbuf), startline, false); - if (script == nullptr) - { - // script parse error? - return 1; + + nd->scr.xs = xs; + nd->scr.ys = ys; } - userfunc_db.put(w3, std::move(script)); + nd->name = script_map.name.data; - return 0; -} + nd->bl_prev = nd->bl_next = nullptr; + nd->bl_m = m; + nd->bl_x = x; + nd->bl_y = y; + nd->bl_id = npc_get_new_npc_id(); + nd->dir = dir; + nd->flag = 0; + nd->npc_class = npc_class; + nd->speed = 200_ms; + nd->scr.script = std::move(script); + nd->option = Opt0::ZERO; + nd->opt1 = Opt1::ZERO; + nd->opt2 = Opt2::ZERO; + nd->opt3 = Opt3::ZERO; -/*========================================== - * mob行解析 - *------------------------------------------ - */ -static -int npc_parse_mob(XString w1, XString, MobName w3, ZString w4) -{ - int x, y, xs, ys, num; - Species mob_class; - int i; - MapName mapname; - NpcEvent eventname; - dumb_ptr md; - - xs = ys = 0; - int delay1_ = 0, delay2_ = 0; - if (!extract(w1, record<',', 3>(&mapname, &x, &y, &xs, &ys)) || - !extract(w4, record<',', 2>(&mob_class, &num, &delay1_, &delay2_, &eventname))) - { - PRINTF("bad monster line : %s\n"_fmt, w3); - return 1; - } - interval_t delay1 = std::chrono::milliseconds(delay1_); - interval_t delay2 = std::chrono::milliseconds(delay2_); + npc_script++; + nd->bl_type = BL::NPC; + nd->npc_subtype = NpcSubtype::SCRIPT; - P m = TRY_UNWRAP(map_mapname2mapid(mapname), return 1); + nd->n = map_addnpc(m, nd); + map_addblock(nd); - if (num > 1 && battle_config.mob_count_rate != 100) - { - if ((num = num * battle_config.mob_count_rate / 100) < 1) - num = 1; - } + clif_spawnnpc(nd); - for (i = 0; i < num; i++) - { - md.new_(); + register_npc_name(nd); - md->bl_prev = nullptr; - md->bl_next = nullptr; - md->bl_m = m; - md->bl_x = x; - md->bl_y = y; - if (w3 == ENGLISH_NAME) - md->name = get_mob_db(mob_class).name; - else if (w3 == JAPANESE_NAME) - md->name = get_mob_db(mob_class).jname; - else - md->name = w3; + for (auto& pair : scriptlabel_db) + npc_convertlabel_db(pair.first, pair.second, nd); - md->n = i; - md->mob_class = mob_class; - md->bl_id = npc_get_new_npc_id(); - md->spawn.m = m; - md->spawn.x0 = x; - md->spawn.y0 = y; - md->spawn.xs = xs; - md->spawn.ys = ys; - md->spawn.delay1 = delay1; - md->spawn.delay2 = delay2; + for (npc_label_list& el : nd->scr.label_listv) + { + ScriptLabel lname = el.name; + int pos = el.pos; - really_memzero_this(&md->state); - // md->timer = nullptr; - md->target_id = BlockId(); - md->attacked_id = BlockId(); + 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); + } + } - md->lootitemv.clear(); + for (npc_label_list& el : 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_); - md->npc_event = eventname; + npc_timerevent_list tel {}; + tel.timer = t; + tel.pos = pos; - md->bl_type = BL::MOB; - map_addiddb(md); - mob_spawn(md->bl_id); + 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); - npc_mob++; + nd->scr.timer_eventv.insert(it, std::move(tel)); + } } + // The counter starts stopped with 0 ticks, which is the first event, + // unless there is none, in which case begin == end. + nd->scr.timer = interval_t::zero(); + nd->scr.next_event = nd->scr.timer_eventv.begin(); + // nd->scr.timerid = nullptr; - return 0; + return true; } -/*========================================== - * マップフラグ行の解析 - *------------------------------------------ - */ static -int npc_parse_mapflag(XString w1, XString, XString w3, ZString w4) +bool npc_load_script_any(ast::npc::Script *script) { - MapName mapname, savemap; - int savex, savey; - - mapname = stringish(w1); - if (!mapname) - return 1; - - P m = TRY_UNWRAP(map_mapname2mapid(mapname), return 1); - - MapFlag mf; - if (!extract(w3, &mf)) - return 1; - - if (battle_config.pk_mode && mf == MapFlag::NOPVP) - { - m->flag.set(MapFlag::NOPVP, 1); - m->flag.set(MapFlag::PVP, 0); - return 0; - } - - if (mf == MapFlag::NOSAVE) + MATCH (*script) { - if (w4 == "SavePoint"_s) + CASE (ast::npc::ScriptFunction&, script_function) { - m->save.map_ = stringish("SavePoint"_s); - m->save.x = -1; - m->save.y = -1; + return npc_load_script_function(script->body, script_function); } - else if (extract(w4, record<','>(&savemap, &savex, &savey))) + CASE (ast::npc::ScriptNone&, script_none) { - m->save.map_ = savemap; - m->save.x = savex; - m->save.y = savey; + return npc_load_script_none(script->body, script_none); } - } - if (mf == MapFlag::RESAVE) - { - if (extract(w4, record<','>(&savemap, &savex, &savey))) + CASE (ast::npc::ScriptMapNone&, script_map_none) { - m->resave.map_ = savemap; - m->resave.x = savex; - m->resave.y = savey; + auto& mapname = script_map_none.m; + Option> m = map_mapname2mapid(mapname.data); + if (m.is_none()) + { + mapname.span.error(STRPRINTF("Map not found: %s"_fmt, mapname.data)); + return false; + } + return npc_load_script_map_none(script->body, script_map_none); + } + CASE (ast::npc::ScriptMap&, script_map) + { + auto& mapname = script_map.m; + Option> m = map_mapname2mapid(mapname.data); + if (m.is_none()) + { + mapname.span.error(STRPRINTF("Map not found: %s"_fmt, mapname.data)); + return false; + } + return npc_load_script_map(script->body, script_map); } } - m->flag.set(mf, true); - - return 0; + abort(); } dumb_ptr npc_spawn_text(Borrowed m, int x, int y, @@ -766,104 +816,103 @@ dumb_ptr npc_spawn_text(Borrowed m, int x, int y, return retval; } -/*========================================== - * npc初期化 - *------------------------------------------ - */ -bool do_init_npc(void) +static +bool load_one_npc(io::LineCharReader& fp, bool& done) { - bool rv = true; + auto res = TRY_UNWRAP(ast::npc::parse_top(fp), { done = true; return true; }); + if (res.get_failure()) + PRINTF("%s\n"_fmt, res.get_failure()); + ast::npc::TopLevel tl = TRY_UNWRAP(std::move(res.get_success()), return false); - for (; !npc_srcs.empty(); npc_srcs.pop_front()) + MATCH (tl) { - AString nsl = npc_srcs.front(); - io::ReadFile fp(nsl); - if (!fp.is_open()) + CASE (ast::npc::Comment&, c) { - PRINTF("file not found : %s\n"_fmt, nsl); - rv = false; - continue; + return true; } - PRINTF("\rLoading NPCs [%d]: %-54s"_fmt, unwrap(npc_id) - unwrap(START_NPC_NUM), - nsl); - int lines = 0; - AString zline; - while (fp.getline(zline)) + CASE (ast::npc::Warp&, warp) { - XString w1, w2, w3, w4x; - ZString w4z; - lines++; - - if (is_comment(zline)) - continue; - - if (!extract(zline, record<'|', 3>(&w1, &w2, &w3, &w4x)) || !w1 || !w2 || !w3) - { - FPRINTF(stderr, "%s:%d: Broken script line: %s\n"_fmt, nsl, lines, zline); - rv = false; - continue; - } - if (&*w4x.end() == &*zline.end()) - { - w4z = zline.xrslice_t(w4x.size()); - } - assert(bool(w4x) == bool(w4z)); - - if (w1 != "-"_s && w1 != "function"_s) - { - auto comma = std::find(w1.begin(), w1.end(), ','); - MapName mapname = stringish(w1.xislice_h(comma)); - Option> m = map_mapname2mapid(mapname); - if (m.is_none()) - { - // "mapname" is not assigned to this server - FPRINTF(stderr, "%s:%d: Map not found: %s\n"_fmt, nsl, lines, mapname); - rv = false; - continue; - } - } - if (w2 == "warp"_s) - { - NpcName npcname = stringish(w3); - rv &= !npc_parse_warp(w1, w2, npcname, w4z); - } - else if (w2 == "shop"_s) + auto& mapname = warp.m; + Option> m = map_mapname2mapid(mapname.data); + if (m.is_none()) { - NpcName npcname = stringish(w3); - rv &= !npc_parse_shop(w1, w2, npcname, w4z); + mapname.span.error(STRPRINTF("Map not found: %s"_fmt, mapname.data)); + return false; } - else if (w2 == "script"_s) - { - if (w1 == "function"_s) - { - rv &= !npc_parse_function(w1, w2, w3, w4z, - w4x, fp, &lines); - } - else - { - NpcName npcname = stringish(w3); - rv &= !npc_parse_script(w1, w2, npcname, w4z, - w4x, fp, &lines); - } - } - else if (w2 == "monster"_s) + return npc_load_warp(warp); + } + CASE (ast::npc::Shop&, shop) + { + auto& mapname = shop.m; + Option> m = map_mapname2mapid(mapname.data); + if (m.is_none()) { - MobName mobname = stringish(w3); - rv &= !npc_parse_mob(w1, w2, mobname, w4z); + mapname.span.error(STRPRINTF("Map not found: %s"_fmt, mapname.data)); + return false; } - else if (w2 == "mapflag"_s) + return npc_load_shop(shop); + } + CASE (ast::npc::Monster&, monster) + { + auto& mapname = monster.m; + Option> m = map_mapname2mapid(mapname.data); + if (m.is_none()) { - rv &= !npc_parse_mapflag(w1, w2, w3, w4z); + mapname.span.error(STRPRINTF("Map not found: %s"_fmt, mapname.data)); + return false; } - else + return npc_load_monster(monster); + } + CASE (ast::npc::MapFlag&, mapflag) + { + auto& mapname = mapflag.m; + Option> m = map_mapname2mapid(mapname.data); + if (m.is_none()) { - PRINTF("odd script line: %s\n"_fmt, zline); - script_errors++; + mapname.span.error(STRPRINTF("Map not found: %s"_fmt, mapname.data)); + return false; } + return npc_load_mapflag(mapflag); } - fflush(stdout); + CASE (ast::npc::Script&, script) + { + return npc_load_script_any(&script); + } + } + abort(); +} + +static +bool load_npc_file(ZString nsl) +{ + io::LineCharReader fp(nsl); + if (!fp.is_open()) + { + PRINTF("file not found : %s\n"_fmt, nsl); + return false; + } + PRINTF("Loading NPCs [%d]: %-54s\r"_fmt, unwrap(npc_id) - unwrap(START_NPC_NUM), + nsl); + + bool done = false; + while (!done) + { + if (!load_one_npc(fp, done)) + return false; + } + return true; +} + +bool do_init_npc(void) +{ + bool rv = true; + + for (; !npc_srcs.empty(); npc_srcs.pop_front()) + { + AString nsl = npc_srcs.front(); + rv &= load_npc_file(nsl); } - PRINTF("\rNPCs Loaded: %d [Warps:%d Shops:%d Scripts:%d Mobs:%d] %20s\n"_fmt, + PRINTF("NPCs Loaded: %d [Warps:%d Shops:%d Scripts:%d Mobs:%d] %20s\n"_fmt, unwrap(npc_id) - unwrap(START_NPC_NUM), npc_warp, npc_shop, npc_script, npc_mob, ""_s); if (script_errors) diff --git a/src/map/npc-parse.hpp b/src/map/npc-parse.hpp index a9cf300..902a214 100644 --- a/src/map/npc-parse.hpp +++ b/src/map/npc-parse.hpp @@ -25,7 +25,7 @@ namespace tmwa { -int npc_parse_warp(XString w1, XString, NpcName w3, XString w4); +bool npc_load_warp(ast::npc::Warp& warp); /** * Spawns and installs a talk-only NPC diff --git a/src/map/script-parse.cpp b/src/map/script-parse.cpp index 7956831..85e29a5 100644 --- a/src/map/script-parse.cpp +++ b/src/map/script-parse.cpp @@ -33,6 +33,8 @@ #include "../mmo/cxxstdio_enums.hpp" +#include "../ast/script.hpp" + #include "map.t.hpp" #include "script-buffer.hpp" #include "script-call.hpp" @@ -692,10 +694,10 @@ void add_builtin_functions(void) } } -std::unique_ptr parse_script(ZString src, int line, bool implicit_end) +std::unique_ptr compile_script(const ast::script::ScriptBody& body, bool implicit_end) { auto script_buf = make_unique(); - script_buf->parse_script(src, line, implicit_end); + script_buf->parse_script(body.braced_body, body.span.begin.line, implicit_end); return std::move(script_buf); } diff --git a/src/map/script-parse.hpp b/src/map/script-parse.hpp index d1f824f..b8b5012 100644 --- a/src/map/script-parse.hpp +++ b/src/map/script-parse.hpp @@ -27,7 +27,7 @@ namespace tmwa { -std::unique_ptr parse_script(ZString, int, bool implicit_end); +std::unique_ptr compile_script(const ast::script::ScriptBody& body, bool implicit_end); extern Map scriptlabel_db; -- cgit v1.2.3-70-g09d2