summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in15
-rw-r--r--src/ast/fwd.hpp12
-rw-r--r--src/ast/item.cpp27
-rw-r--r--src/ast/item.hpp24
-rw-r--r--src/ast/item_test.cpp16
-rw-r--r--src/ast/npc.cpp260
-rw-r--r--src/ast/npc.hpp69
-rw-r--r--src/ast/npc_test.cpp214
-rw-r--r--src/ast/script.hpp4
-rw-r--r--src/compat/result.hpp16
-rw-r--r--src/io/read.cpp5
-rw-r--r--src/main-gdb-head.py18
-rw-r--r--src/map/atcommand.cpp20
-rw-r--r--src/map/fwd.hpp1
-rw-r--r--src/map/itemdb.cpp132
-rw-r--r--src/map/magic-v2.cpp17
-rw-r--r--src/map/map.cpp4
-rw-r--r--src/map/npc-parse.cpp977
-rw-r--r--src/map/npc-parse.hpp2
-rw-r--r--src/map/script-parse.cpp6
-rw-r--r--src/map/script-parse.hpp2
-rw-r--r--src/strings/rstring.cpp2
-rw-r--r--src/strings/rstring.hpp2
23 files changed, 1020 insertions, 825 deletions
diff --git a/Makefile.in b/Makefile.in
index 9be1ad9..8254bc2 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -474,8 +474,8 @@ lib/%.${SO_LONG}-gdb.py:
lib/%.${SO_LONG}: lib/%.${SO_LONG}-gdb.py
$(MKDIR_FIRST)
$l ${CXX} -shared -Wl,-soname=$*.${SO_SHORT} ${LDFLAGS} $(filter-out lib/%-gdb.py,$^) ${LDLIBS} -o $@
- $c ln -sf $*.${SO_LONG} lib/$*.${SO_SHORT}
- $c ln -sf $*.${SO_SHORT} lib/$*.so
+ $c ln -sfT $*.${SO_LONG} lib/$*.${SO_SHORT}
+ $c ln -sfT $*.${SO_SHORT} lib/$*.so
lib/%.a:
$(MKDIR_FIRST)
rm -f $@
@@ -510,9 +510,14 @@ test-gtest: $(patsubst bin/tests/%,stamp/run-%.stamp,${GTEST_BINARIES})
test: test-dtest
test-dtest: $(patsubst bin/tests/%,stamp/run-%.stamp,${DTEST_BINARIES})
+$(patsubst bin/tests/%,stamp/run-%.stamp,${TEST_BINARIES} ${GTEST_BINARIES} ${DTEST_BINARIES}): stamp/symlink-test-lib-dir.stamp
+stamp/symlink-test-lib-dir.stamp:
+ @mkdir -p bin
+ ln -sfT ../lib bin/lib
+ touch $@
+
stamp/run-%.stamp: bin/tests/%
$(MKDIR_FIRST)
- ln -sf ../lib bin/lib
${TESTER} $< ${TEST_ARGS}
touch $@
@@ -566,8 +571,8 @@ ifeq (${ENABLE_SHARED},yes)
${install_data} -t ${DESTDIR}${LIBDIR} \
$(patsubst %.so,%.${SO_LONG},${LIBRARIES})
for lib in $(patsubst lib/%.so,%,${LIBRARIES}); do \
- ln -sf $$lib.${SO_LONG} ${DESTDIR}${LIBDIR}/$$lib.${SO_SHORT}; \
- ln -sf $$lib.${SO_SHORT} ${DESTDIR}${LIBDIR}/$$lib.so; \
+ ln -sfT $$lib.${SO_LONG} ${DESTDIR}${LIBDIR}/$$lib.${SO_SHORT}; \
+ ln -sfT $$lib.${SO_SHORT} ${DESTDIR}${LIBDIR}/$$lib.so; \
done
else
${install_data} -t ${DESTDIR}${LIBDIR} \
diff --git a/src/ast/fwd.hpp b/src/ast/fwd.hpp
index 4ab4490..24bf545 100644
--- a/src/ast/fwd.hpp
+++ b/src/ast/fwd.hpp
@@ -23,11 +23,23 @@
#include "../compat/fwd.hpp" // rank 2
#include "../io/fwd.hpp" // rank 4
#include "../net/fwd.hpp" // rank 5
+#include "../sexpr/fwd.hpp" // rank 5
#include "../mmo/fwd.hpp" // rank 6
#include "../high/fwd.hpp" // rank 9
// ast/fwd.hpp is rank 10
namespace tmwa
{
+namespace ast
+{
+namespace npc
+{
+class Warp;
+} // namespace npc
+namespace script
+{
+class ScriptBody;
+} // namespace script
+} // namespace ast
// meh, add more when I feel like it
} // namespace tmwa
diff --git a/src/ast/item.cpp b/src/ast/item.cpp
index 99417d8..bd4f295 100644
--- a/src/ast/item.cpp
+++ b/src/ast/item.cpp
@@ -18,8 +18,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
-#include "../compat/memory.hpp"
-
#include "../io/extract.hpp"
#include "../io/line.hpp"
@@ -36,9 +34,6 @@ namespace item
{
using io::respan;
- // separate file because virtual
- ItemOrComment::~ItemOrComment() {}
-
static
void skip_comma_space(io::LineCharReader& lr)
{
@@ -107,6 +102,7 @@ namespace item
opt.implicit_start = true;
opt.implicit_end = true;
opt.one_line = true;
+ opt.no_event = true;
auto rv = ast::script::parse_script_body(lr, opt);
if (rv.get_success().is_some())
{
@@ -115,20 +111,19 @@ namespace item
return rv;
}
-#define SPAN_EXTRACT(bitexpr, var) ({ auto bit = bitexpr; if (!extract(bit.data, &var.data)) return Err(bit.span.error_str("failed to extract"_s #var)); var.span = bit.span; })
+#define SPAN_EXTRACT(bitexpr, var) ({ auto bit = bitexpr; if (!extract(bit.data, &var.data)) return Err(bit.span.error_str("failed to extract "_s #var)); var.span = bit.span; })
#define EOL_ERROR(lr) ({ io::LineChar c; lr.get(c) ? Err(c.error_str("unexpected EOL"_s)) : Err("unexpected EOF before unexpected EOL"_s); })
- Result<std::unique_ptr<ItemOrComment>> parse_item(io::LineCharReader& lr)
+ Option<Result<ItemOrComment>> parse_item(io::LineCharReader& lr)
{
- std::unique_ptr<ItemOrComment> rv = nullptr;
- Spanned<RString> first = TRY_UNWRAP(lex_nonscript(lr, true), return Ok(std::move(rv)));
+ Spanned<RString> first = TRY_UNWRAP(lex_nonscript(lr, true), return None);
if (first.data.startswith("//"_s))
{
Comment comment;
- comment.span = first.span;
comment.comment = first.data;
- rv = make_unique<Comment>(std::move(comment));
- return Ok(std::move(rv));
+ ItemOrComment rv = std::move(comment);
+ rv.span = first.span;
+ return Some(Ok(std::move(rv)));
}
Item item;
SPAN_EXTRACT(first, item.id);
@@ -150,10 +145,10 @@ namespace item
SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.view);
item.use_script = TRY(lex_script(lr));
item.equip_script = TRY(lex_script(lr));
- item.span.begin = item.id.span.begin;
- item.span.end = item.equip_script.span.end;
- rv = make_unique<Item>(std::move(item));
- return Ok(std::move(rv));
+ ItemOrComment rv = std::move(item);
+ rv.span.begin = item.id.span.begin;
+ rv.span.end = item.equip_script.span.end;
+ return Some(Ok(std::move(rv)));
}
} // namespace item
} // namespace ast
diff --git a/src/ast/item.hpp b/src/ast/item.hpp
index b54e55c..a8fe908 100644
--- a/src/ast/item.hpp
+++ b/src/ast/item.hpp
@@ -20,12 +20,12 @@
#include "fwd.hpp"
-#include <memory>
-
#include "../compat/result.hpp"
#include "../io/span.hpp"
+#include "../sexpr/variant.hpp"
+
#include "../mmo/clif.t.hpp"
#include "../mmo/ids.hpp"
#include "../mmo/strs.hpp"
@@ -41,17 +41,11 @@ namespace item
{
using io::Spanned;
- struct ItemOrComment
- {
- io::LineSpan span;
-
- virtual ~ItemOrComment();
- };
- struct Comment : ItemOrComment
+ struct Comment
{
RString comment;
};
- struct Item : ItemOrComment
+ struct Item
{
Spanned<ItemNameId> id;
Spanned<ItemName> name;
@@ -74,7 +68,15 @@ namespace item
ast::script::ScriptBody equip_script;
};
- Result<std::unique_ptr<ItemOrComment>> parse_item(io::LineCharReader& lr);
+ using ItemOrCommentBase = Variant<Comment, Item>;
+ struct ItemOrComment : ItemOrCommentBase
+ {
+ ItemOrComment(Comment o) : ItemOrCommentBase(std::move(o)) {}
+ ItemOrComment(Item o) : ItemOrCommentBase(std::move(o)) {}
+ io::LineSpan span;
+ };
+
+ Option<Result<ItemOrComment>> parse_item(io::LineCharReader& lr);
} // namespace item
} // namespace ast
} // namespace tmwa
diff --git a/src/ast/item_test.cpp b/src/ast/item_test.cpp
index 3b5fd07..a77662e 100644
--- a/src/ast/item_test.cpp
+++ b/src/ast/item_test.cpp
@@ -24,7 +24,7 @@
#include "../tests/fdhack.hpp"
-//#include "../poison.hpp"
+#include "../poison.hpp"
namespace tmwa
@@ -54,7 +54,7 @@ namespace item
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
auto res = parse_item(lr);
- EXPECT_EQ(res.get_success(), Some(std::unique_ptr<ItemOrComment>(nullptr)));
+ EXPECT_TRUE(res.is_none());
}
}
TEST(itemast, comment)
@@ -70,11 +70,11 @@ namespace item
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_item(lr);
+ auto res = TRY_UNWRAP(parse_item(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- EXPECT_SPAN(top->span, 1,1, 1,8);
- auto p = dynamic_cast<Comment *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,8);
+ auto p = top.get_if<Comment>();
EXPECT_TRUE(p);
if (p)
{
@@ -96,11 +96,11 @@ namespace item
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_item(lr);
+ auto res = TRY_UNWRAP(parse_item(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- EXPECT_SPAN(top->span, 1,1, 1,58);
- auto p = dynamic_cast<Item *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,58);
+ auto p = top.get_if<Item>();
EXPECT_TRUE(p);
if (p)
{
diff --git a/src/ast/npc.cpp b/src/ast/npc.cpp
index ceb381d..6b75503 100644
--- a/src/ast/npc.cpp
+++ b/src/ast/npc.cpp
@@ -18,8 +18,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
-#include "../compat/memory.hpp"
-
#include "../io/cxxstdio.hpp"
#include "../io/extract.hpp"
#include "../io/line.hpp"
@@ -39,10 +37,7 @@ namespace npc
{
using io::respan;
- // separate file because virtual
- TopLevel::~TopLevel() {}
-
-#define TRY_EXTRACT(bit, var) ({ if (!extract(bit.data, &var.data)) return Err(bit.span.error_str("failed to extract"_s #var)); var.span = bit.span; })
+#define TRY_EXTRACT(bit, var) ({ if (!extract(bit.data, &var.data)) return Err(bit.span.error_str("failed to extract "_s #var)); var.span = bit.span; })
static
Result<Warp> parse_warp(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
@@ -53,28 +48,33 @@ namespace npc
}
if (bits[0].data.size() != 3)
{
- return Err(bits[0].span.error_str("expect 3 ,component,s"_s));
+ return Err(bits[0].span.error_str("in |component 1| expect 3 ,component,s"_s));
}
assert(bits[1].data.size() == 1);
assert(bits[1].data[0].data == "warp"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
if (bits[3].data.size() != 5)
{
- return Err(bits[3].span.error_str("expect 5 ,component,s"_s));
+ return Err(bits[3].span.error_str("in |component 4| expect 5 ,component,s"_s));
}
Warp warp;
- warp.span = span;
TRY_EXTRACT(bits[0].data[0], warp.m);
TRY_EXTRACT(bits[0].data[1], warp.x);
TRY_EXTRACT(bits[0].data[2], warp.y);
warp.key_span = bits[1].data[0].span;
TRY_EXTRACT(bits[2].data[0], warp.name);
+ if (bits[3].data[0].data == "-1"_s)
+ bits[3].data[0].data = "4294967295"_s;
TRY_EXTRACT(bits[3].data[0], warp.xs);
+ warp.xs.data += 2;
+ if (bits[3].data[1].data == "-1"_s)
+ bits[3].data[1].data = "4294967295"_s;
TRY_EXTRACT(bits[3].data[1], warp.ys);
+ warp.ys.data += 2;
TRY_EXTRACT(bits[3].data[2], warp.to_m);
TRY_EXTRACT(bits[3].data[3], warp.to_x);
TRY_EXTRACT(bits[3].data[4], warp.to_y);
@@ -89,21 +89,20 @@ namespace npc
}
if (bits[0].data.size() != 4)
{
- return Err(bits[0].span.error_str("expect 4 ,component,s"_s));
+ return Err(bits[0].span.error_str("in |component 1| expect 4 ,component,s"_s));
}
assert(bits[1].data.size() == 1);
assert(bits[1].data[0].data == "shop"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
if (bits[3].data.size() < 2)
{
- return Err(bits[3].span.error_str("expect at least 2 ,component,s"_s));
+ return Err(bits[3].span.error_str("in |component 4| expect at least 2 ,component,s"_s));
}
Shop shop;
- shop.span = span;
TRY_EXTRACT(bits[0].data[0], shop.m);
TRY_EXTRACT(bits[0].data[1], shop.x);
TRY_EXTRACT(bits[0].data[2], shop.y);
@@ -121,8 +120,8 @@ namespace npc
assert(data.span.begin.line == data.span.end.line);
item.span = data.span;
- XString name;
- if (!extract(data.data, record<':'>(&name, &item.data.value.data)))
+ XString name, value;
+ if (!extract(data.data, record<':'>(&name, &value)))
return Err(data.span.error_str("Failed to split item:value"_s));
item.data.name.span = item.span;
item.data.name.span.end.column = item.data.name.span.begin.column + name.size() - 1;
@@ -138,7 +137,22 @@ namespace npc
item.data.name.span.warning("Shop item is an id; should be a name"_s);
}
if (!extract(name, &item.data.name.data))
- return Err("item name problem (too long?)"_s);
+ {
+ return Err(item.data.name.span.error_str("item name problem (too long?)"_s));
+ }
+ if (value.startswith('-'))
+ {
+ item.data.value.span.begin.warning("Shop value multiplier should use '*' instead of '-' now"_s);
+ value = value.xslice_t(1);
+ }
+ else if (value.startswith('*'))
+ {
+ value = value.xslice_t(1);
+ }
+ if (!extract(value, &item.data.value.data))
+ {
+ return Err(item.data.value.span.error_str("invalid item value"_s));
+ }
}
return Ok(std::move(shop));
}
@@ -151,21 +165,20 @@ namespace npc
}
if (bits[0].data.size() != 3 && bits[0].data.size() != 5)
{
- return Err(bits[0].span.error_str("expect 3 or 5 ,component,s"_s));
+ return Err(bits[0].span.error_str("in |component 1| expect 3 or 5 ,component,s"_s));
}
assert(bits[1].data.size() == 1);
assert(bits[1].data[0].data == "monster"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
if (bits[3].data.size() != 2 && bits[3].data.size() != 4 && bits[3].data.size() != 5)
{
- return Err(bits[3].span.error_str("expect 2, 4, or 5 ,component,s"_s));
+ return Err(bits[3].span.error_str("in |component 4| expect 2, 4, or 5 ,component,s"_s));
}
Monster mob;
- mob.span = span;
TRY_EXTRACT(bits[0].data[0], mob.m);
TRY_EXTRACT(bits[0].data[1], mob.x);
TRY_EXTRACT(bits[0].data[2], mob.y);
@@ -231,42 +244,39 @@ namespace npc
}
if (bits[0].data.size() != 1)
{
- return Err(bits[0].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[0].span.error_str("in |component 1| expect 1 ,component,s"_s));
}
assert(bits[1].data.size() == 1);
assert(bits[1].data[0].data == "mapflag"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
- }
- if (bits.size() >= 4)
- {
- if (bits[3].data.size() != 1)
- {
- return Err(bits[3].span.error_str("expect 1 ,component,s"_s));
- }
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
MapFlag mapflag;
- mapflag.span = span;
TRY_EXTRACT(bits[0].data[0], mapflag.m);
mapflag.key_span = bits[1].data[0].span;
TRY_EXTRACT(bits[2].data[0], mapflag.name);
if (bits.size() >= 4)
{
- TRY_EXTRACT(bits[3].data[0], mapflag.opt_extra);
+ mapflag.vec_extra.span = bits[3].span;
+ for (auto& bit : bits[3].data)
+ {
+ mapflag.vec_extra.data.emplace_back();
+ TRY_EXTRACT(bit, mapflag.vec_extra.data.back());
+ }
}
else
{
- mapflag.opt_extra.data = ""_s;
- mapflag.opt_extra.span = bits[2].span;
- mapflag.opt_extra.span.end.column++;
- mapflag.opt_extra.span.begin.column = mapflag.opt_extra.span.end.column;
+ mapflag.vec_extra.data = {};
+ mapflag.vec_extra.span = bits[2].span;
+ mapflag.vec_extra.span.end.column++;
+ mapflag.vec_extra.span.begin.column = mapflag.vec_extra.span.end.column;
}
return Ok(std::move(mapflag));
}
static
- Result<ScriptFunction> parse_script_function(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
+ Result<ScriptFunction> parse_script_function_head(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
{
// ScriptFunction: function|script|Fun Name{code}
if (bits.size() != 3)
@@ -279,23 +289,17 @@ namespace npc
assert(bits[1].data[0].data == "script"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
ScriptFunction script_function;
- script_function.span = span;
script_function.key1_span = bits[0].data[0].span;
- script_function.key_span = bits[1].data[0].span;
TRY_EXTRACT(bits[2].data[0], script_function.name);
- // also expect '{' and parse real script
- ast::script::ScriptOptions opt;
- opt.implicit_start = true;
- opt.default_label = "OnCall"_s;
- script_function.body = TRY(ast::script::parse_script_body(lr, opt));
+ // also expect '{' and parse real script (in caller)
return Ok(std::move(script_function));
}
static
- Result<ScriptNone> parse_script_none(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
+ Result<ScriptNone> parse_script_none_head(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
{
// ScriptNone: -|script|script name|-1{code}
if (bits.size() != 4)
@@ -308,29 +312,23 @@ namespace npc
assert(bits[1].data[0].data == "script"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
assert(bits[3].data[0].data == "-1"_s);
if (bits[3].data.size() != 1)
{
- return Err(bits[2].span.error_str("last |component| should be just -1"_s));
+ return Err(bits[3].span.error_str("in |component 4| should be just -1"_s));
}
ScriptNone script_none;
- script_none.span = span;
script_none.key1_span = bits[0].data[0].span;
- script_none.key_span = bits[1].data[0].span;
TRY_EXTRACT(bits[2].data[0], script_none.name);
script_none.key4_span = bits[3].data[0].span;
- // also expect '{' and parse real script
- ast::script::ScriptOptions opt;
- opt.implicit_start = true;
- opt.no_start = true;
- script_none.body = TRY(ast::script::parse_script_body(lr, opt));
+ // also expect '{' and parse real script (in caller)
return Ok(std::move(script_none));
}
static
- Result<ScriptMapNone> parse_script_map_none(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
+ Result<ScriptMapNone> parse_script_map_none_head(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
{
// ScriptMapNone: m,x,y,d|script|script name|-1{code}
if (bits.size() != 4)
@@ -339,37 +337,31 @@ namespace npc
}
if (bits[0].data.size() != 4)
{
- return Err(bits[0].span.error_str("expect 3 ,component,s"_s));
+ return Err(bits[0].span.error_str("in |component 1| expect 3 ,component,s"_s));
}
assert(bits[1].data.size() == 1);
assert(bits[1].data[0].data == "script"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
if (bits[3].data.size() != 1 || bits[3].data[0].data != "-1"_s)
{
- return Err(bits[2].span.error_str("last |component| should be just -1"_s));
+ return Err(bits[3].span.error_str("in |component 4| should be just -1"_s));
}
ScriptMapNone script_map_none;
- script_map_none.span = span;
TRY_EXTRACT(bits[0].data[0], script_map_none.m);
TRY_EXTRACT(bits[0].data[1], script_map_none.x);
TRY_EXTRACT(bits[0].data[2], script_map_none.y);
TRY_EXTRACT(bits[0].data[3], script_map_none.d);
- script_map_none.key_span = bits[1].data[0].span;
TRY_EXTRACT(bits[2].data[0], script_map_none.name);
script_map_none.key4_span = bits[3].data[0].span;
- // also expect '{' and parse real script
- ast::script::ScriptOptions opt;
- opt.implicit_start = true;
- opt.no_start = true;
- script_map_none.body = TRY(ast::script::parse_script_body(lr, opt));
+ // also expect '{' and parse real script (in caller)
return Ok(std::move(script_map_none));
}
static
- Result<ScriptMap> parse_script_map(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
+ Result<ScriptMap> parse_script_map_head(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
{
// ScriptMap: m,x,y,d|script|script name|class,xs,ys{code}
if (bits.size() != 4)
@@ -378,41 +370,48 @@ namespace npc
}
if (bits[0].data.size() != 4)
{
- return Err(bits[0].span.error_str("expect 3 ,component,s"_s));
+ return Err(bits[0].span.error_str("in |component 1| expect 3 ,component,s"_s));
}
assert(bits[1].data.size() == 1);
assert(bits[1].data[0].data == "script"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
- if (bits[3].data.size() != 3)
+ if (bits[3].data.size() != 1 && bits[3].data.size() != 3)
{
- return Err(bits[3].span.error_str("expect 3 ,component,s"_s));
+ return Err(bits[3].span.error_str("in |component 4| expect 1 or 3 ,component,s"_s));
}
ScriptMap script_map;
- script_map.span = span;
TRY_EXTRACT(bits[0].data[0], script_map.m);
TRY_EXTRACT(bits[0].data[1], script_map.x);
TRY_EXTRACT(bits[0].data[2], script_map.y);
TRY_EXTRACT(bits[0].data[3], script_map.d);
- script_map.key_span = bits[1].data[0].span;
TRY_EXTRACT(bits[2].data[0], script_map.name);
TRY_EXTRACT(bits[3].data[0], script_map.npc_class);
- TRY_EXTRACT(bits[3].data[1], script_map.xs);
- TRY_EXTRACT(bits[3].data[2], script_map.ys);
- // also expect '{' and parse real script
- ast::script::ScriptOptions opt;
- opt.implicit_start = true;
- opt.default_label = "OnClick"_s;
- script_map.body = TRY(ast::script::parse_script_body(lr, opt));
+ if (bits[3].data.size() >= 3)
+ {
+ TRY_EXTRACT(bits[3].data[1], script_map.xs);
+ script_map.xs.data = script_map.xs.data * 2 + 1;
+ TRY_EXTRACT(bits[3].data[2], script_map.ys);
+ script_map.ys.data = script_map.ys.data * 2 + 1;
+ }
+ else
+ {
+ script_map.xs.data = 0;
+ script_map.xs.span = script_map.npc_class.span;
+ script_map.xs.span.end.column++;
+ script_map.xs.span.begin = script_map.xs.span.end;
+ script_map.ys.data = 0;
+ script_map.ys.span = script_map.xs.span;
+ }
+ // also expect '{' and parse real script (in caller)
return Ok(std::move(script_map));
}
static
- Result<std::unique_ptr<Script>> parse_script_any(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
+ Result<Script> parse_script_any(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
{
- std::unique_ptr<Script> rv;
// 4 cases:
// ScriptFunction: function|script|Fun Name{code}
// ScriptNone: -|script|script name|-1{code}
@@ -420,21 +419,52 @@ namespace npc
// ScriptMap: m,x,y,d|script|script name|class,xs,ys{code}
if (bits[0].data[0].data == "function"_s)
{
- rv = make_unique<ScriptFunction>(TRY(parse_script_function(span, bits, lr)));
+ Script rv = TRY(parse_script_function_head(span, bits));
+ rv.key_span = bits[1].data[0].span;
+
+ ast::script::ScriptOptions opt;
+ opt.implicit_start = true;
+ opt.default_label = "OnCall"_s;
+ opt.no_event = true;
+ rv.body = TRY(ast::script::parse_script_body(lr, opt));
+ return Ok(std::move(rv));
}
else if (bits[0].data[0].data == "-"_s)
{
- rv = make_unique<ScriptNone>(TRY(parse_script_none(span, bits, lr)));
+ Script rv = TRY(parse_script_none_head(span, bits));
+ rv.key_span = bits[1].data[0].span;
+
+ ast::script::ScriptOptions opt;
+ opt.implicit_start = true;
+ opt.no_start = true;
+ rv.body = TRY(ast::script::parse_script_body(lr, opt));
+ return Ok(std::move(rv));
}
else if (bits.size() >= 4 && bits[3].data[0].data == "-1"_s)
{
- rv = make_unique<ScriptMapNone>(TRY(parse_script_map_none(span, bits, lr)));
+ Script rv = TRY(parse_script_map_none_head(span, bits));
+ rv.key_span = bits[1].data[0].span;
+
+ ast::script::ScriptOptions opt;
+ opt.implicit_start = true;
+ opt.no_start = true;
+ rv.body = TRY(ast::script::parse_script_body(lr, opt));
+ return Ok(std::move(rv));
}
else
{
- rv = make_unique<ScriptMap>(TRY(parse_script_map(span, bits, lr)));
+ ScriptMap script_map = TRY(parse_script_map_head(span, bits));
+ bool no_touch = !script_map.xs.data && !script_map.ys.data;
+ Script rv = std::move(script_map);
+ rv.key_span = bits[1].data[0].span;
+
+ ast::script::ScriptOptions opt;
+ opt.implicit_start = true;
+ opt.default_label = "OnClick"_s;
+ opt.no_touch = no_touch;
+ rv.body = TRY(ast::script::parse_script_body(lr, opt));
+ return Ok(std::move(rv));
}
- return Ok(std::move(rv));
}
/// Try to extract a top-level token
@@ -460,10 +490,29 @@ namespace npc
{
return None;
}
- // separators are simple
+ // separators are simple ... NOT.
+ // Reasonably, we should only ever eat a single char here.
+ // Unfortunately, we aren't dealing with reasonable people here.
if (c.ch() == '|' || c.ch() == ',')
{
lr.adv();
+ while (true)
+ {
+ io::LineChar c2;
+ if (!lr.get(c2) || c2.ch() == '\n' || c2.ch() == '{')
+ {
+ c.warning("Separator at EOL"_s);
+ return None;
+ }
+ if (c2.ch() == ',' || c2.ch() == '|')
+ {
+ lr.adv();
+ c = c2;
+ c.warning("Adjacent separators"_s);
+ continue;
+ }
+ break;
+ }
return Some(respan({c, c}, RString(VString<1>(c.ch()))));
}
io::LineSpan span;
@@ -488,7 +537,7 @@ namespace npc
// if first token on line, can get comment
if (first && c.ch() == '/')
{
- while (lr.get(c) && c.ch() != '\n' && c.ch() != '{')
+ while (lr.get(c) && c.ch() != '\n')
{
accum += c.ch();
span.end = c;
@@ -506,11 +555,11 @@ namespace npc
return Some(respan(span, RString(accum)));
}
- Result<std::unique_ptr<TopLevel>> parse_top(io::LineCharReader& in)
+ Option<Result<TopLevel>> parse_top(io::LineCharReader& in)
{
- std::unique_ptr<TopLevel> rv;
Spanned<std::vector<Spanned<std::vector<Spanned<RString>>>>> bits;
+ // special logic for the first 'bit'
{
Spanned<RString> mayc = TRY_UNWRAP(lex(in, true),
{
@@ -519,15 +568,15 @@ namespace npc
{
return Err(c.error_str("Unexpected script open"_s));
}
- return Ok(std::move(rv));
+ return None;
});
if (mayc.data.startswith("//"_s))
{
Comment com;
com.comment = std::move(mayc.data);
- com.span = std::move(mayc.span);
- rv = make_unique<Comment>(std::move(com));
- return Ok(std::move(rv));
+ TopLevel rv = std::move(com);
+ rv.span = std::move(mayc.span);
+ return Some(Ok(std::move(rv)));
}
if (mayc.data == "|"_s || mayc.data == ","_s)
@@ -573,29 +622,38 @@ namespace npc
Spanned<RString>& w2 = bits.data[1].data[0];
if (w2.data == "warp"_s)
{
- rv = make_unique<Warp>(TRY(parse_warp(bits.span, bits.data)));
+ TopLevel rv = TRY(parse_warp(bits.span, bits.data));
+ rv.span = bits.span;
+ return Some(Ok(std::move(rv)));
}
else if (w2.data == "shop"_s)
{
- rv = make_unique<Shop>(TRY(parse_shop(bits.span, bits.data)));
+ TopLevel rv = TRY(parse_shop(bits.span, bits.data));
+ rv.span = bits.span;
+ return Some(Ok(std::move(rv)));
}
else if (w2.data == "monster"_s)
{
- rv = make_unique<Monster>(TRY(parse_monster(bits.span, bits.data)));
+ TopLevel rv = TRY(parse_monster(bits.span, bits.data));
+ rv.span = bits.span;
+ return Some(Ok(std::move(rv)));
}
else if (w2.data == "mapflag"_s)
{
- rv = make_unique<MapFlag>(TRY(parse_mapflag(bits.span, bits.data)));
+ TopLevel rv = TRY(parse_mapflag(bits.span, bits.data));
+ rv.span = bits.span;
+ return Some(Ok(std::move(rv)));
}
else if (w2.data == "script"_s)
{
- rv = TRY_MOVE(parse_script_any(bits.span, bits.data, in));
+ TopLevel rv = TRY_MOVE(parse_script_any(bits.span, bits.data, in));
+ rv.span = bits.span;
+ return Some(Ok(std::move(rv)));
}
else
{
return Err(w2.span.error_str("Unknown type"_s));
}
- return Ok(std::move(rv));
}
} // namespace npc
} // namespace ast
diff --git a/src/ast/npc.hpp b/src/ast/npc.hpp
index dd323b6..a51acd3 100644
--- a/src/ast/npc.hpp
+++ b/src/ast/npc.hpp
@@ -20,18 +20,18 @@
#include "fwd.hpp"
-#include <memory>
-
#include "../compat/result.hpp"
#include "../io/span.hpp"
+#include "../net/timer.t.hpp"
+
+#include "../sexpr/variant.hpp"
+
#include "../mmo/clif.t.hpp"
#include "../mmo/ids.hpp"
#include "../mmo/strs.hpp"
-#include "../net/timer.t.hpp"
-
#include "script.hpp"
@@ -43,17 +43,11 @@ namespace npc
{
using io::Spanned;
- struct TopLevel
- {
- io::LineSpan span;
-
- virtual ~TopLevel();
- };
- struct Comment : TopLevel
+ struct Comment
{
RString comment;
};
- struct Warp : TopLevel
+ struct Warp
{
Spanned<MapName> m;
Spanned<unsigned> x, y;
@@ -66,9 +60,10 @@ namespace npc
struct ShopItem
{
Spanned<ItemName> name;
+ bool value_multiply;
Spanned<int> value;
};
- struct Shop : TopLevel
+ struct Shop
{
Spanned<MapName> m;
Spanned<unsigned> x, y;
@@ -78,7 +73,7 @@ namespace npc
Spanned<Species> npc_class;
Spanned<std::vector<Spanned<ShopItem>>> items;
};
- struct Monster : TopLevel
+ struct Monster
{
Spanned<MapName> m;
Spanned<unsigned> x, y;
@@ -90,32 +85,25 @@ namespace npc
Spanned<interval_t> delay1, delay2;
Spanned<NpcEvent> event;
};
- struct MapFlag : TopLevel
+ struct MapFlag
{
Spanned<MapName> m;
io::LineSpan key_span;
- // TODO should this extract all the way?
Spanned<RString> name;
- Spanned<RString> opt_extra;
- };
- struct Script : TopLevel
- {
- io::LineSpan key_span;
- // see src/script/parser.hpp
- ast::script::ScriptBody body;
+ Spanned<std::vector<Spanned<RString>>> vec_extra;
};
- struct ScriptFunction : Script
+ struct ScriptFunction
{
io::LineSpan key1_span;
Spanned<RString> name;
};
- struct ScriptNone : Script
+ struct ScriptNone
{
io::LineSpan key1_span;
Spanned<NpcName> name;
io::LineSpan key4_span;
};
- struct ScriptMapNone : Script
+ struct ScriptMapNone
{
Spanned<MapName> m;
Spanned<unsigned> x, y;
@@ -123,7 +111,7 @@ namespace npc
Spanned<NpcName> name;
io::LineSpan key4_span;
};
- struct ScriptMap : Script
+ struct ScriptMap
{
Spanned<MapName> m;
Spanned<unsigned> x, y;
@@ -132,9 +120,32 @@ namespace npc
Spanned<Species> npc_class;
Spanned<unsigned> xs, ys;
};
- // other Script subclasses elsewhere? (for item and magic scripts)
+ using ScriptBase = Variant<ScriptFunction, ScriptNone, ScriptMapNone, ScriptMap>;
+ struct Script : ScriptBase
+ {
+ Script() = default;
+ Script(ScriptFunction s) : ScriptBase(std::move(s)) {}
+ Script(ScriptNone s) : ScriptBase(std::move(s)) {}
+ Script(ScriptMapNone s) : ScriptBase(std::move(s)) {}
+ Script(ScriptMap s) : ScriptBase(std::move(s)) {}
+
+ io::LineSpan key_span;
+ ast::script::ScriptBody body;
+ };
+ using TopLevelBase = Variant<Comment, Warp, Shop, Monster, MapFlag, Script>;
+ struct TopLevel : TopLevelBase
+ {
+ TopLevel() = default;
+ TopLevel(Comment t) : TopLevelBase(std::move(t)) {}
+ TopLevel(Warp t) : TopLevelBase(std::move(t)) {}
+ TopLevel(Shop t) : TopLevelBase(std::move(t)) {}
+ TopLevel(Monster t) : TopLevelBase(std::move(t)) {}
+ TopLevel(MapFlag t) : TopLevelBase(std::move(t)) {}
+ TopLevel(Script t) : TopLevelBase(std::move(t)) {}
+ io::LineSpan span;
+ };
- Result<std::unique_ptr<TopLevel>> parse_top(io::LineCharReader& in);
+ Option<Result<TopLevel>> parse_top(io::LineCharReader& in);
} // namespace npc
} // namespace ast
} // namespace tmwa
diff --git a/src/ast/npc_test.cpp b/src/ast/npc_test.cpp
index ea4bdf3..7cced35 100644
--- a/src/ast/npc_test.cpp
+++ b/src/ast/npc_test.cpp
@@ -24,7 +24,7 @@
#include "../tests/fdhack.hpp"
-//#include "../poison.hpp"
+#include "../poison.hpp"
namespace tmwa
@@ -54,7 +54,7 @@ namespace npc
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
auto res = parse_top(lr);
- EXPECT_EQ(res.get_success(), Some(std::unique_ptr<TopLevel>(nullptr)));
+ EXPECT_TRUE(res.is_none());
}
}
TEST(npcast, comment)
@@ -70,13 +70,11 @@ namespace npc
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,8);
- auto p = dynamic_cast<Comment *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,8);
+ auto p = top.get_if<Comment>();
EXPECT_TRUE(p);
if (p)
{
@@ -91,21 +89,19 @@ namespace npc
{
// 1 2 3 4
//234567890123456789012345678901234567890123456789
- "map.gat,1,2|warp|To Other Map|3,4,other.gat,5,6"_s,
- "map.gat,1,2|warp|To Other Map|3,4,other.gat,5,6\n"_s,
- "map.gat,1,2|warp|To Other Map|3,4,other.gat,5,6{"_s,
+ "map.gat,1,2|warp|To Other Map|3,4,other.gat,7,8"_s,
+ "map.gat,1,2|warp|To Other Map|3,4,other.gat,7,8\n"_s,
+ "map.gat,1,2|warp|To Other Map|3,4,other.gat,7,8{"_s,
// no optional fields in warp
};
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,47);
- auto p = dynamic_cast<Warp *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,47);
+ auto p = top.get_if<Warp>();
EXPECT_TRUE(p);
if (p)
{
@@ -119,15 +115,15 @@ namespace npc
EXPECT_SPAN(p->name.span, 1,18, 1,29);
EXPECT_EQ(p->name.data, stringish<NpcName>("To Other Map"_s));
EXPECT_SPAN(p->xs.span, 1,31, 1,31);
- EXPECT_EQ(p->xs.data, 3);
+ EXPECT_EQ(p->xs.data, 5);
EXPECT_SPAN(p->ys.span, 1,33, 1,33);
- EXPECT_EQ(p->ys.data, 4);
+ EXPECT_EQ(p->ys.data, 6);
EXPECT_SPAN(p->to_m.span, 1,35, 1,43);
EXPECT_EQ(p->to_m.data, stringish<MapName>("other"_s));
EXPECT_SPAN(p->to_x.span, 1,45, 1,45);
- EXPECT_EQ(p->to_x.data, 5);
+ EXPECT_EQ(p->to_x.data, 7);
EXPECT_SPAN(p->to_y.span, 1,47, 1,47);
- EXPECT_EQ(p->to_y.data, 6);
+ EXPECT_EQ(p->to_y.data, 8);
}
}
}
@@ -146,13 +142,11 @@ namespace npc
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,54);
- auto p = dynamic_cast<Shop *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,54);
+ auto p = top.get_if<Shop>();
EXPECT_TRUE(p);
if (p)
{
@@ -213,13 +207,11 @@ namespace npc
bool third = input.startswith('n');
assert(first + second + third == 1);
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,first?65:54);
- auto p = dynamic_cast<Monster *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,first?65:54);
+ auto p = top.get_if<Monster>();
EXPECT_TRUE(p);
if (p)
{
@@ -309,42 +301,63 @@ namespace npc
"Map.gat|mapflag|flagname|optval"_s,
"Map.gat|mapflag|flagname|optval\n"_s,
"Map.gat|mapflag|flagname|optval{"_s,
+ "nap.gat|mapflag|flagname|aa,b,c"_s,
+ "nap.gat|mapflag|flagname|aa,b,c\n"_s,
+ "nap.gat|mapflag|flagname|aa,b,c{"_s,
};
for (auto input : inputs)
{
+ bool first = input.startswith('m');
bool second = input.startswith('M');
+ bool third = input.startswith('n');
+ EXPECT_EQ(first + second + third, 1);
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,!second?24:31);
- auto p = dynamic_cast<MapFlag *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,first?24:31);
+ auto p = top.get_if<MapFlag>();
EXPECT_TRUE(p);
if (p)
{
EXPECT_SPAN(p->m.span, 1,1, 1,7);
- if (!second)
+ if (first)
{
EXPECT_EQ(p->m.data, stringish<MapName>("map"_s));
}
- else
+ if (second)
{
EXPECT_EQ(p->m.data, stringish<MapName>("Map"_s));
}
+ if (third)
+ {
+ EXPECT_EQ(p->m.data, stringish<MapName>("nap"_s));
+ }
EXPECT_SPAN(p->key_span, 1,9, 1,15);
EXPECT_SPAN(p->name.span, 1,17, 1,24);
EXPECT_EQ(p->name.data, "flagname"_s);
- if (!second)
+ if (first)
{
- EXPECT_SPAN(p->opt_extra.span, 1,25, 1,25);
- EXPECT_EQ(p->opt_extra.data, ""_s);
+ EXPECT_SPAN(p->vec_extra.span, 1,25, 1,25);
+ EXPECT_EQ(p->vec_extra.data.size(), 0);
}
- else
+ if (second)
{
- EXPECT_SPAN(p->opt_extra.span, 1,26, 1,31);
- EXPECT_EQ(p->opt_extra.data, "optval"_s);
+ EXPECT_SPAN(p->vec_extra.span, 1,26, 1,31);
+ EXPECT_EQ(p->vec_extra.data.size(), 1);
+ EXPECT_SPAN(p->vec_extra.data[0].span, 1,26, 1,31);
+ EXPECT_EQ(p->vec_extra.data[0].data, "optval"_s);
+ }
+ if (third)
+ {
+ EXPECT_SPAN(p->vec_extra.span, 1,26, 1,31);
+ EXPECT_EQ(p->vec_extra.data.size(), 3);
+ EXPECT_SPAN(p->vec_extra.data[0].span, 1,26, 1,27);
+ EXPECT_EQ(p->vec_extra.data[0].data, "aa"_s);
+ EXPECT_SPAN(p->vec_extra.data[1].span, 1,29, 1,29);
+ EXPECT_EQ(p->vec_extra.data[1].data, "b"_s);
+ EXPECT_SPAN(p->vec_extra.data[2].span, 1,31, 1,31);
+ EXPECT_EQ(p->vec_extra.data[2].data, "c"_s);
}
}
}
@@ -366,37 +379,37 @@ namespace npc
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,24);
- auto p = dynamic_cast<ScriptFunction *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,24);
+ auto script = top.get_if<Script>();
+ EXPECT_TRUE(script);
+ auto p = script->get_if<ScriptFunction>();
EXPECT_TRUE(p);
if (p)
{
EXPECT_SPAN(p->key1_span, 1,1, 1,8);
- EXPECT_SPAN(p->key_span, 1,10, 1,15);
+ EXPECT_SPAN(script->key_span, 1,10, 1,15);
EXPECT_SPAN(p->name.span, 1,17, 1,24);
EXPECT_EQ(p->name.data, "Fun Name"_s);
if (input.endswith('}'))
{
- EXPECT_SPAN(p->body.span, 1,25, 1,30);
+ EXPECT_SPAN(script->body.span, 1,25, 1,30);
}
else if (input.endswith('\n'))
{
- EXPECT_SPAN(p->body.span, 2,1, 2,6);
+ EXPECT_SPAN(script->body.span, 2,1, 2,6);
}
else if (input.endswith(' '))
{
- EXPECT_SPAN(p->body.span, 3,2, 3,7);
+ EXPECT_SPAN(script->body.span, 3,2, 3,7);
}
else
{
FAIL();
}
- EXPECT_EQ(p->body.braced_body, "{end;}"_s);
+ EXPECT_EQ(script->body.braced_body, "{end;}"_s);
}
}
}
@@ -416,38 +429,38 @@ namespace npc
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,19);
- auto p = dynamic_cast<ScriptNone *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,19);
+ auto script = top.get_if<Script>();
+ EXPECT_TRUE(script);
+ auto p = script->get_if<ScriptNone>();
EXPECT_TRUE(p);
if (p)
{
EXPECT_SPAN(p->key1_span, 1,1, 1,1);
- EXPECT_SPAN(p->key_span, 1,3, 1,8);
+ EXPECT_SPAN(script->key_span, 1,3, 1,8);
EXPECT_SPAN(p->name.span, 1,10, 1,16);
EXPECT_EQ(p->name.data, stringish<NpcName>("#config"_s));
EXPECT_SPAN(p->key4_span, 1,18, 1,19);
if (input.endswith('}'))
{
- EXPECT_SPAN(p->body.span, 1,20, 1,25);
+ EXPECT_SPAN(script->body.span, 1,20, 1,25);
}
else if (input.endswith('\n'))
{
- EXPECT_SPAN(p->body.span, 2,1, 2,6);
+ EXPECT_SPAN(script->body.span, 2,1, 2,6);
}
else if (input.endswith(' '))
{
- EXPECT_SPAN(p->body.span, 3,2, 3,7);
+ EXPECT_SPAN(script->body.span, 3,2, 3,7);
}
else
{
FAIL();
}
- EXPECT_EQ(p->body.braced_body, "{end;}"_s);
+ EXPECT_EQ(script->body.braced_body, "{end;}"_s);
}
}
}
@@ -465,13 +478,13 @@ namespace npc
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,28);
- auto p = dynamic_cast<ScriptMapNone *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,28);
+ auto script = top.get_if<Script>();
+ EXPECT_TRUE(script);
+ auto p = script->get_if<ScriptMapNone>();
EXPECT_TRUE(p);
if (p)
{
@@ -483,27 +496,27 @@ namespace npc
EXPECT_EQ(p->y.data, 2);
EXPECT_SPAN(p->d.span, 1,13, 1,13);
EXPECT_EQ(p->d.data, DIR::NW);
- EXPECT_SPAN(p->key_span, 1,15, 1,20);
+ EXPECT_SPAN(script->key_span, 1,15, 1,20);
EXPECT_SPAN(p->name.span, 1,22, 1,25);
EXPECT_EQ(p->name.data, stringish<NpcName>("Init"_s));
EXPECT_SPAN(p->key4_span, 1,27, 1,28);
if (input.endswith('}'))
{
- EXPECT_SPAN(p->body.span, 1,29, 1,34);
+ EXPECT_SPAN(script->body.span, 1,29, 1,34);
}
else if (input.endswith('\n'))
{
- EXPECT_SPAN(p->body.span, 2,1, 2,6);
+ EXPECT_SPAN(script->body.span, 2,1, 2,6);
}
else if (input.endswith(' '))
{
- EXPECT_SPAN(p->body.span, 3,2, 3,7);
+ EXPECT_SPAN(script->body.span, 3,2, 3,7);
}
else
{
FAIL();
}
- EXPECT_EQ(p->body.braced_body, "{end;}"_s);
+ EXPECT_EQ(script->body.braced_body, "{end;}"_s);
}
}
}
@@ -517,54 +530,77 @@ namespace npc
"map.gat,1,2,3|script|Asdf|4,5,6{end;}"_s,
"map.gat,1,2,3|script|Asdf|4,5,6\n{end;}\n"_s,
"map.gat,1,2,3|script|Asdf|4,5,6\n \n {end;} "_s,
+ "Map.gat,1,2,3|script|Asdf|40506{end;}"_s,
+ "Map.gat,1,2,3|script|Asdf|40506\n{end;}\n"_s,
+ "Map.gat,1,2,3|script|Asdf|40506\n \n {end;} "_s,
};
for (auto input : inputs)
{
+ bool second = input.startswith('M');
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,31);
- auto p = dynamic_cast<ScriptMap *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,31);
+ auto script = top.get_if<Script>();
+ EXPECT_TRUE(script);
+ auto p = script->get_if<ScriptMap>();
EXPECT_TRUE(p);
if (p)
{
EXPECT_SPAN(p->m.span, 1,1, 1,7);
- EXPECT_EQ(p->m.data, stringish<MapName>("map"_s));
+ if (!second)
+ {
+ EXPECT_EQ(p->m.data, stringish<MapName>("map"_s));
+ }
+ else
+ {
+ EXPECT_EQ(p->m.data, stringish<MapName>("Map"_s));
+ }
EXPECT_SPAN(p->x.span, 1,9, 1,9);
EXPECT_EQ(p->x.data, 1);
EXPECT_SPAN(p->y.span, 1,11, 1,11);
EXPECT_EQ(p->y.data, 2);
EXPECT_SPAN(p->d.span, 1,13, 1,13);
EXPECT_EQ(p->d.data, DIR::NW);
- EXPECT_SPAN(p->key_span, 1,15, 1,20);
+ EXPECT_SPAN(script->key_span, 1,15, 1,20);
EXPECT_SPAN(p->name.span, 1,22, 1,25);
EXPECT_EQ(p->name.data, stringish<NpcName>("Asdf"_s));
- EXPECT_SPAN(p->npc_class.span, 1,27, 1,27);
- EXPECT_EQ(p->npc_class.data, wrap<Species>(4));
- EXPECT_SPAN(p->xs.span, 1,29, 1,29);
- EXPECT_EQ(p->xs.data, 5);
- EXPECT_SPAN(p->ys.span, 1,31, 1,31);
- EXPECT_EQ(p->ys.data, 6);
+ if (!second)
+ {
+ EXPECT_SPAN(p->npc_class.span, 1,27, 1,27);
+ EXPECT_EQ(p->npc_class.data, wrap<Species>(4));
+ EXPECT_SPAN(p->xs.span, 1,29, 1,29);
+ EXPECT_EQ(p->xs.data, 11);
+ EXPECT_SPAN(p->ys.span, 1,31, 1,31);
+ EXPECT_EQ(p->ys.data, 13);
+ }
+ else
+ {
+ EXPECT_SPAN(p->npc_class.span, 1,27, 1,31);
+ EXPECT_EQ(p->npc_class.data, wrap<Species>(40506));
+ EXPECT_SPAN(p->xs.span, 1,32, 1,32);
+ EXPECT_EQ(p->xs.data, 0);
+ EXPECT_SPAN(p->ys.span, 1,32, 1,32);
+ EXPECT_EQ(p->ys.data, 0);
+ }
if (input.endswith('}'))
{
- EXPECT_SPAN(p->body.span, 1,32, 1,37);
+ EXPECT_SPAN(script->body.span, 1,32, 1,37);
}
else if (input.endswith('\n'))
{
- EXPECT_SPAN(p->body.span, 2,1, 2,6);
+ EXPECT_SPAN(script->body.span, 2,1, 2,6);
}
else if (input.endswith(' '))
{
- EXPECT_SPAN(p->body.span, 3,2, 3,7);
+ EXPECT_SPAN(script->body.span, 3,2, 3,7);
}
else
{
FAIL();
}
- EXPECT_EQ(p->body.braced_body, "{end;}"_s);
+ EXPECT_EQ(script->body.braced_body, "{end;}"_s);
}
}
}
diff --git a/src/ast/script.hpp b/src/ast/script.hpp
index da43f90..74b11e1 100644
--- a/src/ast/script.hpp
+++ b/src/ast/script.hpp
@@ -51,6 +51,10 @@ namespace script
bool implicit_end = false;
// forbid newlines anywhere between { and }
bool one_line = false;
+ // forbid the OnTouch event
+ bool no_touch = false;
+ // forbid all events
+ bool no_event = false;
};
Result<ScriptBody> parse_script_body(io::LineCharReader& lr, ScriptOptions opt);
diff --git a/src/compat/result.hpp b/src/compat/result.hpp
index f03c026..6adc552 100644
--- a/src/compat/result.hpp
+++ b/src/compat/result.hpp
@@ -66,7 +66,23 @@ namespace tmwa
{
return Result<T>(magic_flag, message);
}
+ template<class T>
+ operator Option<Result<T>>()
+ {
+ return Some(Result<T>(magic_flag, message));
+ }
};
+
+ template<class T>
+ bool operator == (const Result<T>& l, const Result<T>& r)
+ {
+ return l.get_success() == r.get_success() && l.get_failure() == r.get_failure();
+ }
+ template<class T>
+ bool operator != (const Result<T>& l, const Result<T>& r)
+ {
+ return !(l == r);
+ }
} // namespace result
using result::Result;
using result::Ok;
diff --git a/src/io/read.cpp b/src/io/read.cpp
index d701c7f..32974d6 100644
--- a/src/io/read.cpp
+++ b/src/io/read.cpp
@@ -118,6 +118,7 @@ namespace io
}
bool ReadFile::getline(AString& line)
{
+ bool was_real_file = fd != FD();
MString tmp;
char c;
bool anything = false;
@@ -151,8 +152,10 @@ namespace io
else
FPRINTF(stderr, "warning: file contains bare CR\n"_fmt);
}
- else if (!happy && anything)
+ else if (!happy && anything && was_real_file)
+ {
FPRINTF(stderr, "warning: file does not contain a trailing newline\n"_fmt);
+ }
line = AString(tmp);
return anything;
}
diff --git a/src/main-gdb-head.py b/src/main-gdb-head.py
index 3a05917..a465c97 100644
--- a/src/main-gdb-head.py
+++ b/src/main-gdb-head.py
@@ -134,20 +134,30 @@ class PointerPrinter(object):
def to_string(self):
v = self._value
- addr = int(v.cast(gdb.lookup_type('uintptr_t')))
+ uptr = gdb.lookup_type('uintptr_t')
+ addr = int(v.cast(uptr))
if not addr:
s = 'nullptr'
else:
try:
sym, off, sec, lib = info_symbol(addr)
- except:
- s = '(%s)<heap/stack 0x%x>' % (v.type, addr)
+ except TypeError:
+ sp = gdb.parse_and_eval('$sp')
+ sp = int(sp.cast(uptr))
+ LOTS = 8 * 1024 * 1024
+ diff = addr - sp
+ if +diff >= 0 and +diff <= LOTS:
+ a = '<$sp+0x%x>' % +diff
+ elif -diff >= 0 and -diff <= LOTS:
+ a = '<$sp-0x%x>' % -diff
+ else:
+ a = '<heap 0x%x>' % addr
+ s = '(%s)%s' % (v.type, a)
else:
if off:
s = '<%s+%d>' % off
else:
s = '<%s>' % sym
- # TODO should I add (type *) ?
return s
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<map_session_data> 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<NpcName>(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<struct item_data> 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<struct item_data> 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<const ScriptBuffer> 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<const ScriptBuffer> 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<AString> 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<npc_data> 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<npc_data_warp> 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<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return 1);
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), abort());
+ dumb_ptr<npc_data_warp> 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<struct item_data> id = ((extract(name_or_id, &itv->nameid) && itv->nameid)
- ? ({
- P<struct item_data> id_ = itemdb_search(itv->nameid);
- id_;
- })
- : ({
- P<struct item_data> 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<npc_data_shop> 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>(dir_);
- P<map_local> 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<map_local> 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<item_data> id = ((extract(it.data.name.data, &back.nameid) && back.nameid)
+ ? ({
+ P<item_data> id_ = TRY_UNWRAP(itemdb_exists(back.nameid), { it.data.name.span.error("No item with this numerical id"_s); return false; });
+ id_;
+ })
+ : ({
+ P<item_data> 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<npc_data_script> 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<map_local> 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<mob_data> 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<const ScriptBuffer> script = nullptr;
- dumb_ptr<npc_data_script> nd;
- int evflag = 0;
+ MapName mapname = mapflag.m.data;
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), abort());
- P<map_local> 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>(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<npc_data_script> 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<const ScriptBuffer> 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<const ScriptBuffer> script = compile_script(body, false);
+ if (script == nullptr)
+ return false;
+ dumb_ptr<npc_data_script> 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<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));
+ }
}
+ // 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<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname),
+ {
+ script_map_none.m.span.error("No such map"_s);
+ return false;
+ });
+
+ std::unique_ptr<const ScriptBuffer> script = compile_script(body, false);
+ if (script == nullptr)
+ return false;
+
+ dumb_ptr<npc_data_script> 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<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname),
+ {
+ script_map.m.span.error("No such map"_s);
+ return false;
+ });
+
+ std::unique_ptr<const ScriptBuffer> script = compile_script(body, false);
+ if (script == nullptr)
+ return false;
+
+
+ dumb_ptr<npc_data_script> 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<const ScriptBuffer> 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<mob_data> 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<map_local> 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<interval_t>(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<MapName>(w1);
- if (!mapname)
- return 1;
-
- P<map_local> 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<MapName>("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<P<map_local>> 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<P<map_local>> 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_data> npc_spawn_text(Borrowed<map_local> m, int x, int y,
@@ -766,104 +816,103 @@ dumb_ptr<npc_data> npc_spawn_text(Borrowed<map_local> 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<BlockId>(npc_id) - unwrap<BlockId>(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<MapName>(w1.xislice_h(comma));
- Option<P<map_local>> 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<NpcName>(w3);
- rv &= !npc_parse_warp(w1, w2, npcname, w4z);
- }
- else if (w2 == "shop"_s)
+ auto& mapname = warp.m;
+ Option<P<map_local>> m = map_mapname2mapid(mapname.data);
+ if (m.is_none())
{
- NpcName npcname = stringish<NpcName>(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<NpcName>(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<P<map_local>> m = map_mapname2mapid(mapname.data);
+ if (m.is_none())
{
- MobName mobname = stringish<MobName>(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<P<map_local>> 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<P<map_local>> 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<BlockId>(npc_id) - unwrap<BlockId>(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<BlockId>(npc_id) - unwrap<BlockId>(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<const ScriptBuffer> parse_script(ZString src, int line, bool implicit_end)
+std::unique_ptr<const ScriptBuffer> compile_script(const ast::script::ScriptBody& body, bool implicit_end)
{
auto script_buf = make_unique<ScriptBuffer>();
- 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<const ScriptBuffer> parse_script(ZString, int, bool implicit_end);
+std::unique_ptr<const ScriptBuffer> compile_script(const ast::script::ScriptBody& body, bool implicit_end);
extern
Map<ScriptLabel, int> scriptlabel_db;
diff --git a/src/strings/rstring.cpp b/src/strings/rstring.cpp
index 5675935..aaf0ba0 100644
--- a/src/strings/rstring.cpp
+++ b/src/strings/rstring.cpp
@@ -36,7 +36,7 @@ namespace tmwa
{
namespace strings
{
- RString::RString()
+ RString::RString() noexcept
: u{.begin= ""}, maybe_end(u.begin)
{
}
diff --git a/src/strings/rstring.hpp b/src/strings/rstring.hpp
index ad44beb..62f74fa 100644
--- a/src/strings/rstring.hpp
+++ b/src/strings/rstring.hpp
@@ -48,7 +48,7 @@ namespace strings
const char *maybe_end;
public:
- RString();
+ RString() noexcept;
RString(LString s);
RString(const RString&);
RString(RString&&);