summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/admin/fwd.hpp13
-rw-r--r--src/admin/globals.cpp48
-rw-r--r--src/admin/globals.hpp (renamed from src/ints/cmp.cpp)17
-rw-r--r--src/admin/ladmin.cpp518
-rw-r--r--src/admin/main.cpp2
-rw-r--r--src/ast/fwd.hpp45
-rw-r--r--src/ast/item.cpp155
-rw-r--r--src/ast/item.hpp82
-rw-r--r--src/ast/item_test.cpp150
-rw-r--r--src/ast/npc.cpp620
-rw-r--r--src/ast/npc.hpp142
-rw-r--r--src/ast/npc_test.cpp556
-rw-r--r--src/ast/quest.cpp125
-rw-r--r--src/ast/quest.hpp65
-rw-r--r--src/ast/script.cpp75
-rw-r--r--src/ast/script.hpp112
-rw-r--r--src/char/char.cpp645
-rw-r--r--src/char/char.hpp25
-rw-r--r--src/char/consts.hpp (renamed from src/map/magic-interpreter.cpp)12
-rw-r--r--src/char/fwd.hpp25
-rw-r--r--src/char/globals.cpp68
-rw-r--r--src/char/globals.hpp59
-rw-r--r--src/char/int_party.cpp159
-rw-r--r--src/char/int_party.hpp11
-rw-r--r--src/char/int_storage.cpp55
-rw-r--r--src/char/int_storage.hpp13
-rw-r--r--src/char/inter.cpp80
-rw-r--r--src/char/inter.hpp20
-rw-r--r--src/char/main.cpp2
-rw-r--r--src/compat/attr.hpp4
-rw-r--r--src/compat/borrow.hpp111
-rw-r--r--src/compat/borrow.py18
-rw-r--r--src/compat/fwd.hpp15
-rw-r--r--src/compat/nullpo.hpp10
-rw-r--r--src/compat/operators.hpp (renamed from src/generic/operators.hpp)0
-rw-r--r--src/compat/option.hpp469
-rw-r--r--src/compat/option.py39
-rw-r--r--src/compat/option_test.cpp499
-rw-r--r--src/compat/rawmem.hpp21
-rw-r--r--src/compat/result.hpp94
-rw-r--r--src/compat/result_test.cpp79
-rw-r--r--src/compat/time_t.cpp26
-rw-r--r--src/compat/time_t.hpp84
-rw-r--r--src/diagnostics.hpp3
-rw-r--r--src/generic/array.hpp9
-rw-r--r--src/generic/db.cpp26
-rw-r--r--src/generic/db.hpp40
-rw-r--r--src/generic/dumb_ptr.cpp26
-rw-r--r--src/generic/dumb_ptr.hpp5
-rw-r--r--src/generic/enum.hpp3
-rw-r--r--src/generic/fwd.hpp32
-rw-r--r--src/generic/matrix.cpp26
-rw-r--r--src/generic/md5.hpp1
-rw-r--r--src/generic/operators.cpp26
-rw-r--r--src/generic/random.hpp4
-rw-r--r--src/generic/random2.hpp4
-rw-r--r--src/high/core.cpp (renamed from src/mmo/core.cpp)18
-rw-r--r--src/high/core.hpp (renamed from src/mmo/core.hpp)2
-rw-r--r--src/high/extract_mmo.cpp (renamed from src/mmo/extract.cpp)43
-rw-r--r--src/high/extract_mmo.hpp (renamed from src/compat/rawmem.cpp)15
-rw-r--r--src/high/fwd.hpp40
-rw-r--r--src/high/md5more.cpp (renamed from src/mmo/md5more.cpp)2
-rw-r--r--src/high/md5more.hpp (renamed from src/mmo/md5more.hpp)4
-rw-r--r--src/high/mmo.hpp (renamed from src/mmo/mmo.hpp)16
-rw-r--r--src/high/utils.cpp (renamed from src/mmo/utils.cpp)39
-rw-r--r--src/high/utils.hpp (renamed from src/compat/cast.cpp)11
-rw-r--r--src/ints/fwd.hpp2
-rw-r--r--src/ints/little.cpp26
-rw-r--r--src/ints/udl.cpp26
-rw-r--r--src/ints/wrap.cpp26
-rw-r--r--src/ints/wrap.py5
-rw-r--r--src/io/cxxstdio.cpp26
-rw-r--r--src/io/cxxstdio_enums.cpp26
-rw-r--r--src/io/dir.hpp2
-rw-r--r--src/io/extract.cpp220
-rw-r--r--src/io/extract.hpp (renamed from src/mmo/extract.hpp)59
-rw-r--r--src/io/extract_test.cpp (renamed from src/mmo/extract_test.cpp)94
-rw-r--r--src/io/fd.cpp5
-rw-r--r--src/io/fd.hpp2
-rw-r--r--src/io/fwd.hpp13
-rw-r--r--src/io/line.cpp107
-rw-r--r--src/io/line.hpp46
-rw-r--r--src/io/line_test.cpp66
-rw-r--r--src/io/read.cpp47
-rw-r--r--src/io/read.hpp14
-rw-r--r--src/io/read_test.cpp67
-rw-r--r--src/io/span.cpp113
-rw-r--r--src/io/span.hpp92
-rw-r--r--src/io/tty.cpp27
-rw-r--r--src/io/write.cpp2
-rw-r--r--src/io/write.hpp2
-rw-r--r--src/login/consts.hpp (renamed from src/map/magic-expr-eval.cpp)13
-rw-r--r--src/login/fwd.hpp22
-rw-r--r--src/login/globals.cpp54
-rw-r--r--src/login/globals.hpp (renamed from src/mmo/consts.cpp)28
-rw-r--r--src/login/login.cpp1188
-rw-r--r--src/login/login.hpp60
-rw-r--r--src/login/login.t.hpp24
-rw-r--r--src/login/main.cpp2
-rw-r--r--src/main-gdb-head.py89
-rw-r--r--src/map/atcommand.cpp762
-rw-r--r--src/map/atcommand.hpp14
-rw-r--r--src/map/battle.cpp628
-rw-r--r--src/map/battle.hpp132
-rw-r--r--src/map/battle.t.hpp3
-rw-r--r--src/map/chrif.cpp370
-rw-r--r--src/map/chrif.hpp23
-rw-r--r--src/map/clif.cpp399
-rw-r--r--src/map/clif.hpp42
-rw-r--r--src/map/consts.hpp (renamed from src/generic/intern-pool.cpp)14
-rw-r--r--src/map/fwd.hpp37
-rw-r--r--src/map/globals.cpp149
-rw-r--r--src/map/globals.hpp109
-rw-r--r--src/map/grfio.cpp13
-rw-r--r--src/map/grfio.hpp7
-rw-r--r--src/map/intif.cpp20
-rw-r--r--src/map/intif.hpp13
-rw-r--r--src/map/itemdb.cpp173
-rw-r--r--src/map/itemdb.hpp17
-rw-r--r--src/map/magic-expr-eval.hpp3
-rw-r--r--src/map/magic-expr.cpp198
-rw-r--r--src/map/magic-expr.hpp13
-rw-r--r--src/map/magic-expr.py8
-rw-r--r--src/map/magic-interpreter-base.cpp27
-rw-r--r--src/map/magic-interpreter-base.hpp12
-rw-r--r--src/map/magic-interpreter.hpp23
-rw-r--r--src/map/magic-interpreter.py212
-rw-r--r--src/map/magic-interpreter.t.hpp3
-rw-r--r--src/map/magic-stmt.cpp85
-rw-r--r--src/map/magic-stmt.hpp9
-rw-r--r--src/map/magic-stmt.py8
-rw-r--r--src/map/magic-v2.cpp51
-rw-r--r--src/map/magic-v2.hpp3
-rw-r--r--src/map/magic.cpp6
-rw-r--r--src/map/magic.hpp9
-rw-r--r--src/map/main.cpp5
-rw-r--r--src/map/map.cpp523
-rw-r--r--src/map/map.hpp151
-rw-r--r--src/map/map.py34
-rw-r--r--src/map/map.t.hpp10
-rw-r--r--src/map/mapflag.cpp7
-rw-r--r--src/map/mapflag.hpp7
-rw-r--r--src/map/mapflag.py10
-rw-r--r--src/map/mob.cpp151
-rw-r--r--src/map/mob.hpp16
-rw-r--r--src/map/mob.t.hpp3
-rw-r--r--src/map/npc-internal.hpp (renamed from src/compat/iter.cpp)19
-rw-r--r--src/map/npc-parse.cpp807
-rw-r--r--src/map/npc-parse.hpp44
-rw-r--r--src/map/npc.cpp956
-rw-r--r--src/map/npc.hpp27
-rw-r--r--src/map/party.cpp143
-rw-r--r--src/map/party.hpp15
-rw-r--r--src/map/path.cpp17
-rw-r--r--src/map/path.hpp5
-rw-r--r--src/map/pc.cpp978
-rw-r--r--src/map/pc.hpp27
-rw-r--r--src/map/pc.t.hpp3
-rw-r--r--src/map/quest.cpp135
-rw-r--r--src/map/quest.hpp (renamed from src/sexpr/bind.cpp)34
-rw-r--r--src/map/script-buffer.hpp45
-rw-r--r--src/map/script-call-internal.hpp100
-rw-r--r--src/map/script-call-internal.tcc79
-rw-r--r--src/map/script-call.cpp926
-rw-r--r--src/map/script-call.hpp67
-rw-r--r--src/map/script-call.t.hpp48
-rw-r--r--src/map/script-fun.cpp3164
-rw-r--r--src/map/script-fun.hpp (renamed from src/sexpr/variant.cpp)33
-rw-r--r--src/map/script-parse-internal.hpp67
-rw-r--r--src/map/script-parse.cpp851
-rw-r--r--src/map/script-parse.hpp (renamed from src/tests/test.cpp)23
-rw-r--r--src/map/script-parse.py553
-rw-r--r--src/map/script-persist.hpp128
-rw-r--r--src/map/script-persist.py37
-rw-r--r--src/map/script-startup-internal.hpp36
-rw-r--r--src/map/script-startup.cpp265
-rw-r--r--src/map/script-startup.hpp (renamed from src/compat/memory.cpp)18
-rw-r--r--src/map/script.cpp5100
-rw-r--r--src/map/script.hpp277
-rw-r--r--src/map/script.py25
-rw-r--r--src/map/skill-pools.cpp22
-rw-r--r--src/map/skill-pools.hpp3
-rw-r--r--src/map/skill.cpp68
-rw-r--r--src/map/skill.hpp18
-rw-r--r--src/map/storage.cpp62
-rw-r--r--src/map/storage.hpp13
-rw-r--r--src/map/tmw.cpp34
-rw-r--r--src/map/tmw.hpp7
-rw-r--r--src/map/trade.cpp43
-rw-r--r--src/map/trade.hpp7
-rw-r--r--src/mmo/clif.t.hpp (renamed from src/map/clif.t.hpp)10
-rw-r--r--src/mmo/config_parse.cpp211
-rw-r--r--src/mmo/config_parse.hpp7
-rw-r--r--src/mmo/config_parse_test.cpp60
-rw-r--r--src/mmo/consts.hpp7
-rw-r--r--src/mmo/cxxstdio_enums.hpp (renamed from src/io/cxxstdio_enums.hpp)51
-rw-r--r--src/mmo/enums.cpp26
-rw-r--r--src/mmo/enums.hpp25
-rw-r--r--src/mmo/extract_enums.cpp33
-rw-r--r--src/mmo/extract_enums.hpp50
-rw-r--r--src/mmo/fwd.hpp36
-rw-r--r--src/mmo/human_time_diff.cpp42
-rw-r--r--src/mmo/human_time_diff.hpp45
-rw-r--r--src/mmo/human_time_diff_test.cpp2
-rw-r--r--src/mmo/ids.cpp16
-rw-r--r--src/mmo/ids.hpp18
-rw-r--r--src/mmo/ids.py1
-rw-r--r--src/mmo/login.t.hpp44
-rw-r--r--src/mmo/mmo.cpp26
-rw-r--r--src/mmo/skill.t.hpp (renamed from src/map/skill.t.hpp)3
-rw-r--r--src/mmo/strs.cpp6
-rw-r--r--src/mmo/strs.hpp33
-rw-r--r--src/mmo/utils.hpp167
-rw-r--r--src/mmo/version.cpp33
-rw-r--r--src/mmo/version.hpp9
-rw-r--r--src/monitor/main.cpp257
-rw-r--r--src/net/fwd.hpp9
-rw-r--r--src/net/ip.cpp34
-rw-r--r--src/net/ip.hpp8
-rw-r--r--src/net/ip_test.cpp1
-rw-r--r--src/net/socket.cpp16
-rw-r--r--src/net/socket.hpp19
-rw-r--r--src/net/timer.hpp3
-rw-r--r--src/net/timer.py117
-rw-r--r--src/net/timestamp-utils.cpp72
-rw-r--r--src/net/timestamp-utils.hpp54
-rw-r--r--src/proto-base/fwd.hpp (renamed from src/generic/array.cpp)12
-rw-r--r--src/proto-base/net-array.hpp53
-rw-r--r--src/proto-base/net-neutral.hpp38
-rw-r--r--src/proto-base/net-skewed-length.hpp46
-rw-r--r--src/proto-base/net-string.hpp87
-rw-r--r--src/range/fwd.hpp2
-rw-r--r--src/range/slice.cpp26
-rw-r--r--src/sanity.hpp10
-rw-r--r--src/sexpr/fwd.hpp4
-rw-r--r--src/sexpr/lexer.hpp10
-rw-r--r--src/sexpr/lexer_test.cpp32
-rw-r--r--src/sexpr/parser.hpp2
-rw-r--r--src/sexpr/parser_test.cpp25
-rw-r--r--src/sexpr/union_test.cpp (renamed from src/sexpr/union.cpp)2
-rw-r--r--src/sexpr/variant.hpp49
-rw-r--r--src/sexpr/variant.tcc3
-rw-r--r--src/sexpr/variant_test.cpp32
-rw-r--r--src/sexpr/void.cpp29
-rw-r--r--src/strings/astring.py2
-rw-r--r--src/strings/fwd.hpp2
-rw-r--r--src/strings/rstring.cpp21
-rw-r--r--src/strings/rstring.hpp2
-rw-r--r--src/strings/rstring.py7
-rw-r--r--src/strings/strings2_test.cpp9
-rw-r--r--src/strings/vstring.cpp29
-rw-r--r--src/strings/xstring.py12
-rw-r--r--src/strings/zstring.py12
-rw-r--r--src/tests/fdhack.cpp26
-rw-r--r--src/tests/fwd.hpp4
-rw-r--r--src/warnings.hpp3
-rw-r--r--src/wire/fwd.hpp (renamed from src/generic/enum.cpp)14
-rw-r--r--src/wire/packets.cpp (renamed from src/net/packets.cpp)20
-rw-r--r--src/wire/packets.hpp (renamed from src/net/packets.hpp)9
259 files changed, 16709 insertions, 13675 deletions
diff --git a/src/admin/fwd.hpp b/src/admin/fwd.hpp
index 46674c8..0beaf50 100644
--- a/src/admin/fwd.hpp
+++ b/src/admin/fwd.hpp
@@ -20,8 +20,21 @@
#include "../sanity.hpp"
+#include "../strings/fwd.hpp" // rank 1
+#include "../io/fwd.hpp" // rank 4
+#include "../net/fwd.hpp" // rank 5
+#include "../mmo/fwd.hpp" // rank 6
+#include "../proto2/fwd.hpp" // rank 8
+#include "../high/fwd.hpp" // rank 9
+#include "../wire/fwd.hpp" // rank 9
+// admin/fwd.hpp is rank ∞ because it is an executable
+
namespace tmwa
{
+namespace admin
+{
+ struct AdminConf;
// meh, add more when I feel like it
+} // namespace admin
} // namespace tmwa
diff --git a/src/admin/globals.cpp b/src/admin/globals.cpp
new file mode 100644
index 0000000..e61128a
--- /dev/null
+++ b/src/admin/globals.cpp
@@ -0,0 +1,48 @@
+#include "globals.hpp"
+// globals.cpp - Evil global variables for tmwa-admin.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "../strings/tstring.hpp"
+
+#include "../mmo/ids.hpp"
+
+#include "admin_conf.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+ namespace admin
+ {
+ bool eathena_interactive_session;
+ AdminConf admin_conf;
+ Session *login_session;
+ // flag to know if we waiting bytes from login-server
+ bool bytes_to_read = false;
+ // needs to be global since it's passed to the parse function
+ // really should be added to session data
+ TString parameters;
+ // parameters to display a list of accounts
+ AccountId list_first, list_last;
+ int list_type, list_count;
+ // sometimes, the exit function is called twice... so, don't log twice the message
+ bool already_exit_function = false;
+ } // namespace admin
+} // namespace tmwa
diff --git a/src/ints/cmp.cpp b/src/admin/globals.hpp
index 94ff0e3..3935499 100644
--- a/src/ints/cmp.cpp
+++ b/src/admin/globals.hpp
@@ -1,5 +1,5 @@
-#include "cmp.hpp"
-// cmp.cpp - comparison related operations
+#pragma once
+// globals.hpp - Evil global variables for tmwa-admin.
//
// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
//
@@ -18,9 +18,20 @@
// 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 "../poison.hpp"
+#include "fwd.hpp"
namespace tmwa
{
+ namespace admin
+ {
+ extern bool eathena_interactive_session;
+ extern AdminConf admin_conf;
+ extern Session *login_session;
+ extern bool bytes_to_read;
+ extern TString parameters;
+ extern AccountId list_first, list_last;
+ extern int list_type, list_count;
+ extern bool already_exit_function;
+ } // namespace admin
} // namespace tmwa
diff --git a/src/admin/ladmin.cpp b/src/admin/ladmin.cpp
index 9dae089..e0d6b06 100644
--- a/src/admin/ladmin.cpp
+++ b/src/admin/ladmin.cpp
@@ -35,49 +35,48 @@
#include "../strings/vstring.hpp"
#include "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
#include "../io/read.hpp"
+#include "../io/span.hpp"
#include "../io/tty.hpp"
#include "../io/write.hpp"
#include "../net/ip.hpp"
-#include "../net/packets.hpp"
+#include "../net/timestamp-utils.hpp"
#include "../proto2/any-user.hpp"
#include "../proto2/login-admin.hpp"
#include "../mmo/config_parse.hpp"
-#include "../mmo/core.hpp"
#include "../mmo/human_time_diff.hpp"
-#include "../mmo/mmo.hpp"
-#include "../mmo/utils.hpp"
+#include "../high/mmo.hpp"
#include "../mmo/version.hpp"
+#include "../high/core.hpp"
+#include "../high/utils.hpp"
+
+#include "../wire/packets.hpp"
+
+#include "admin_conf.hpp"
+#include "globals.hpp"
+
#include "../poison.hpp"
namespace tmwa
{
-static
-int eathena_interactive_session;
-#define Iprintf if (eathena_interactive_session) PRINTF
-
-//-------------------------------INSTRUCTIONS------------------------------
-// Set the variables below:
-// IP of the login server.
-// Port where the login-server listens incoming packets.
-// Password of administration (same of config_athena.conf).
-// IMPORTANT:
-// Be sure that you authorize remote administration in login-server
-// (see login_athena.conf, 'admin_state' parameter)
-//-------------------------------------------------------------------------
-static
-IP4Address login_ip = IP4_LOCALHOST; // IP of login-server
-static
-int login_port = 6900; // Port of login-server
-static
-AccountPass admin_pass = stringish<AccountPass>("admin"_s); // Administration password
-static
-AString ladmin_log_filename = "log/ladmin.log"_s;
+DIAG_PUSH();
+DIAG_I(missing_noreturn);
+void SessionDeleter::operator()(SessionData *)
+{
+ assert(false && "ladmin does not have sessions"_s);
+}
+DIAG_POP();
+
+namespace admin
+{
+#define Iprintf if (tmwa::admin::eathena_interactive_session) PRINTF
+
//-------------------------------------------------------------------------
// LIST of COMMANDs that you can type at the prompt:
// To use these commands you can only type only the first letters.
@@ -220,29 +219,6 @@ AString ladmin_log_filename = "log/ladmin.log"_s;
// all other values are 'No MSG', then use state 9 please.
// 'error_message_#7': message of the code error 6 = Your are Prohibited to log in until %s (packet 0x006a)
//
-// timeadd <account_name> <modifier>
-// Adds or substracts time from the validity limit of an account.
-// Modifier is done as follows:
-// Adjustment value (-1, 1, +1, etc...)
-// Modified element:
-// a or y: year
-// m: month
-// j or d: day
-// h: hour
-// mn: minute
-// s: second
-// <example> timeadd testname +1m-2mn1s-6y
-// this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time.
-// NOTE: You can not modify a unlimited validity limit.
-// If you want modify it, you want probably create a limited validity limit.
-// So, at first, you must set the validity limit to a date/time.
-//
-// timeset <account_name> yyyy/mm/dd [hh:mm:ss]
-// Changes the validity limit of an account.
-// Default time [hh:mm:ss]: 23:59:59.
-// timeset <account_name> 0
-// Gives an unlimited validity limit (0 = unlimited).
-//
// unban/unbanish <account name>
// Unban an account.
// Like banset <account name> 0.
@@ -258,37 +234,16 @@ AString ladmin_log_filename = "log/ladmin.log"_s;
// Displays complete information of an account.
//
//-------------------------------------------------------------------------
-static
-Session *login_session;
-static
-int bytes_to_read = 0; // flag to know if we waiting bytes from login-server
-static
-TString parameters; // needs to be global since it's passed to the parse function
-// really should be added to session data
-static
-AccountId list_first, list_last;
-static
-int list_type, list_count; // parameter to display a list of accounts
-static
-int already_exit_function = 0; // sometimes, the exit function is called twice... so, don't log twice the message
-
-DIAG_PUSH();
-DIAG_I(missing_noreturn);
-void SessionDeleter::operator()(SessionData *)
-{
- assert(false && "ladmin does not have sessions"_s);
-}
-DIAG_POP();
//------------------------------
// Writing function of logs file
//------------------------------
#define LADMIN_LOG(fmt, ...) \
- ladmin_log(STRPRINTF(fmt, ## __VA_ARGS__))
+ tmwa::admin::ladmin_log(STRPRINTF(fmt, ## __VA_ARGS__))
static
void ladmin_log(XString line)
{
- io::AppendFile logfp(ladmin_log_filename);
+ io::AppendFile logfp(admin_conf.ladmin_log_filename);
if (!logfp.is_open())
return;
log_with_timestamp(logfp, line);
@@ -300,9 +255,9 @@ void delete_fromlogin(Session *)
login_session = nullptr;
{
PRINTF("Impossible to have a connection with the login-server [%s:%d] !\n"_fmt,
- login_ip, login_port);
+ admin_conf.login_ip, admin_conf.login_port);
LADMIN_LOG("Impossible to have a connection with the login-server [%s:%d] !\n"_fmt,
- login_ip, login_port);
+ admin_conf.login_ip, admin_conf.login_port);
exit(0);
}
}
@@ -487,13 +442,6 @@ void display_help(ZString param)
PRINTF(" Research by name is not possible with this command.\n"_fmt);
PRINTF(" <example> list 10 9999999\n"_fmt);
}
- else if (command == "itemfrob"_s)
- {
- PRINTF("itemfrob <source-id> <dest-id>\n"_fmt);
- PRINTF(" Translates item IDs for all accounts.\n"_fmt);
- PRINTF(" Any items matching the source item ID will be mapped to the dest-id.\n"_fmt);
- PRINTF(" <example> itemfrob 500 700\n"_fmt);
- }
else if (command == "listban"_s)
{
PRINTF("listban [start_id [end_id]]\n"_fmt);
@@ -565,34 +513,6 @@ void display_help(ZString param)
PRINTF(" 'error_message_#7': message of the code error 6\n"_fmt);
PRINTF(" = Your are Prohibited to log in until... (packet 0x006a)\n"_fmt);
}
- else if (command == "timeadd"_s)
- {
- PRINTF("timeadd <account_name> <modifier>\n"_fmt);
- PRINTF(" Adds or substracts time from the validity limit of an account.\n"_fmt);
- PRINTF(" Modifier is done as follows:\n"_fmt);
- PRINTF(" Adjustment value (-1, 1, +1, etc...)\n"_fmt);
- PRINTF(" Modified element:\n"_fmt);
- PRINTF(" a or y: year\n"_fmt);
- PRINTF(" m: month\n"_fmt);
- PRINTF(" j or d: day\n"_fmt);
- PRINTF(" h: hour\n"_fmt);
- PRINTF(" mn: minute\n"_fmt);
- PRINTF(" s: second\n"_fmt);
- PRINTF(" <example> timeadd testname +1m-2mn1s-6y\n"_fmt);
- PRINTF(" this example adds 1 month and 1 second, and substracts 2 minutes\n"_fmt);
- PRINTF(" and 6 years at the same time.\n"_fmt);
- PRINTF("NOTE: You can not modify a unlimited validity limit.\n"_fmt);
- PRINTF(" If you want modify it, you want probably create a limited validity limit.\n"_fmt);
- PRINTF(" So, at first, you must set the validity limit to a date/time.\n"_fmt);
- }
- else if (command == "timeadd"_s)
- {
- PRINTF("timeset <account_name> yyyy/mm/dd [hh:mm:ss]\n"_fmt);
- PRINTF(" Changes the validity limit of an account.\n"_fmt);
- PRINTF(" Default time [hh:mm:ss]: 23:59:59.\n"_fmt);
- PRINTF("timeset <account_name> 0\n"_fmt);
- PRINTF(" Gives an unlimited validity limit (0 = unlimited).\n"_fmt);
- }
else if (command == "unban"_s)
{
PRINTF("unban/unbanish <account name>\n"_fmt);
@@ -644,7 +564,6 @@ void display_help(ZString param)
PRINTF(" gm <account_name> [GM_level] -- Modify the GM level of an account\n"_fmt);
PRINTF(" id <account name> -- Give the id of an account\n"_fmt);
PRINTF(" info <account_id> -- Display all information of an account\n"_fmt);
- PRINTF(" itemfrob <source-id> <dest-id> -- Map all items from one item ID to another\n"_fmt);
PRINTF(" kami <message> -- Sends a broadcast message (in yellow)\n"_fmt);
PRINTF(" kamib <message> -- Sends a broadcast message (in blue)\n"_fmt);
PRINTF(" list [First_id [Last_id]] -- Display a list of accounts\n"_fmt);
@@ -661,10 +580,6 @@ void display_help(ZString param)
PRINTF(" search <expression> -- Seek accounts\n"_fmt);
PRINTF(" sex <nomcompte> <sexe> -- Modify the sex of an account\n"_fmt);
PRINTF(" state <account_name> <new_state> <error_message_#7> -- Change the state\n"_fmt);
- PRINTF(" timeadd <account_name> <modifier> -- Add or substract time from the\n"_fmt);
- PRINTF(" example: ta apple +1m-2mn1s-2y validity limit of an account\n"_fmt);
- PRINTF(" timeset <account_name> yyyy/mm/dd [hh:mm:ss] -- Change the validify limit\n"_fmt);
- PRINTF(" timeset <account_name> 0 -- Give a unlimited validity limit\n"_fmt);
PRINTF(" unban <account name> -- Remove the banishment of an account\n"_fmt);
PRINTF(" unblock <account name> -- Set state 0 (Account ok) to an account\n"_fmt);
PRINTF(" version -- Gives the version of the login-server\n"_fmt);
@@ -1260,29 +1175,6 @@ void listaccount(ZString param, int type)
list_count = 0;
}
-//--------------------------------------------------------
-// Sub-function: Frobnicate items
-//--------------------------------------------------------
-static
-int itemfrob(ZString param)
-{
- ItemNameId source_id, dest_id;
-
- if (!extract(param, record<' '>(&source_id, &dest_id)))
- {
- PRINTF("You must provide the source and destination item IDs.\n"_fmt);
- return 1;
- }
-
- Packet_Fixed<0x7924> fixed_24;
- fixed_24.source_item_id = source_id;
- fixed_24.dest_item_id = dest_id;
- send_fpacket<0x7924, 10>(login_session, fixed_24);
- bytes_to_read = 1; // all logging is done to the three main servers
-
- return 0;
-}
-
//--------------------------------------------
// Sub-function: Asking to modify a memo field
//--------------------------------------------
@@ -1552,179 +1444,6 @@ void blockaccount(ZString param)
changestatesub(name, 5, "-"_s); // state 5, no error message
}
-//---------------------------------------------------------------------
-// Sub-function: Add/substract time to the validity limit of an account
-//---------------------------------------------------------------------
-static
-void timeaddaccount(ZString param)
-{
- AccountName name;
- HumanTimeDiff modif {};
-
- if (!qsplit(param, &name, &modif))
- {
- PRINTF("Please input an account name and a modifier.\n"_fmt);
- PRINTF(" <example>: timeadd testname +1m-2mn1s-6y\n"_fmt);
- PRINTF(" this example adds 1 month and 1 second, and substracts 2 minutes\n"_fmt);
- PRINTF(" and 6 years at the same time.\n"_fmt);
- LADMIN_LOG("Incomplete parameters to modify a limit time ('timeadd' command).\n"_fmt);
- return;
- }
- if (name.is_print())
- {
- return;
- }
-
- if (!modif)
- {
- PRINTF("Please give an adjustment with this command:\n"_fmt);
- PRINTF(" Adjustment value (-1, 1, +1, etc...)\n"_fmt);
- PRINTF(" Modified element:\n"_fmt);
- PRINTF(" a or y: year\n"_fmt);
- PRINTF(" m: month\n"_fmt);
- PRINTF(" j or d: day\n"_fmt);
- PRINTF(" h: hour\n"_fmt);
- PRINTF(" mn: minute\n"_fmt);
- PRINTF(" s: second\n"_fmt);
- PRINTF(" <example> timeadd testname +1m-2mn1s-6y\n"_fmt);
- PRINTF(" this example adds 1 month and 1 second, and substracts 2 minutes\n"_fmt);
- PRINTF(" and 6 years at the same time.\n"_fmt);
- LADMIN_LOG("No adjustment isn't an adjustment ('timeadd' command).\n"_fmt);
- return;
- }
-
- LADMIN_LOG("Request to login-server to modify a time limit.\n"_fmt);
-
- Packet_Fixed<0x7950> fixed_50;
- fixed_50.account_name = name;
- fixed_50.valid_add = modif;
- send_fpacket<0x7950, 38>(login_session, fixed_50);
- bytes_to_read = 1;
-}
-
-//-------------------------------------------------
-// Sub-function: Set a validity limit of an account
-//-------------------------------------------------
-static
-void timesetaccount(ZString param)
-{
- AccountName name;
- XString date;
- XString time_;
- int year, month, day, hour, minute, second;
-
- year = month = day = hour = minute = second = 0;
-
- // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
- TimeT connect_until_time = TimeT();
- struct tm tmtime = connect_until_time; // initialize
-
- if (!qsplit(param, &name, &date, &time_)
- && !qsplit(param, &name, &date))
- {
- PRINTF("Please input an account name, a date and a hour.\n"_fmt);
- PRINTF("<example>: timeset <account_name> yyyy/mm/dd [hh:mm:ss]\n"_fmt);
- PRINTF(" timeset <account_name> 0 (0 = unlimited)\n"_fmt);
- PRINTF(" Default time [hh:mm:ss]: 23:59:59.\n"_fmt);
- LADMIN_LOG("Incomplete parameters to set a limit time ('timeset' command).\n"_fmt);
- return;
- }
- if (!name.is_print())
- return;
-
- if (!time_)
- time_ = "23:59:59"_s;
-
- if (date != "0"_s
- && ((!extract(date, record<'/'>(&year, &month, &day))
- && !extract(date, record<'-'>(&year, &month, &day))
- && !extract(date, record<'.'>(&year, &month, &day)))
- || !extract(time_, record<':'>(&hour, &minute, &second))))
- {
- PRINTF("Please input 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"_fmt);
- LADMIN_LOG("Invalid format for the date/time ('timeset' command).\n"_fmt);
- return;
- }
-
- if (date == "0"_s)
- {
- connect_until_time = TimeT();
- }
- else
- {
- if (year < 70)
- {
- year = year + 100;
- }
- if (year >= 1900)
- {
- year = year - 1900;
- }
- if (month < 1 || month > 12)
- {
- PRINTF("Please give a correct value for the month (from 1 to 12).\n"_fmt);
- LADMIN_LOG("Invalid month for the date ('timeset' command).\n"_fmt);
- return;
- }
- month = month - 1;
- if (day < 1 || day > 31)
- {
- PRINTF("Please give a correct value for the day (from 1 to 31).\n"_fmt);
- LADMIN_LOG("Invalid day for the date ('timeset' command).\n"_fmt);
- return;
- }
- if (((month == 3 || month == 5 || month == 8 || month == 10)
- && day > 30) ||(month == 1 && day > 29))
- {
- PRINTF("Please give a correct value for a day of this month (%d).\n"_fmt,
- month);
- LADMIN_LOG("Invalid day for this month ('timeset' command).\n"_fmt);
- return;
- }
- if (hour < 0 || hour > 23)
- {
- PRINTF("Please give a correct value for the hour (from 0 to 23).\n"_fmt);
- LADMIN_LOG("Invalid hour for the time ('timeset' command).\n"_fmt);
- return;
- }
- if (minute < 0 || minute > 59)
- {
- PRINTF("Please give a correct value for the minutes (from 0 to 59).\n"_fmt);
- LADMIN_LOG("Invalid minute for the time ('timeset' command).\n"_fmt);
- return;
- }
- if (second < 0 || second > 59)
- {
- PRINTF("Please give a correct value for the seconds (from 0 to 59).\n"_fmt);
- LADMIN_LOG("Invalid second for the time ('timeset' command).\n"_fmt);
- return;
- }
- tmtime.tm_year = year;
- tmtime.tm_mon = month;
- tmtime.tm_mday = day;
- tmtime.tm_hour = hour;
- tmtime.tm_min = minute;
- tmtime.tm_sec = second;
- tmtime.tm_isdst = -1; // -1: no winter/summer time modification
- connect_until_time = tmtime;
- if (connect_until_time.error())
- {
- PRINTF("Invalid date.\n"_fmt);
- PRINTF("Please add 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"_fmt);
- LADMIN_LOG("Invalid date. ('timeset' command).\n"_fmt);
- return;
- }
- }
-
- LADMIN_LOG("Request to login-server to set a time limit.\n"_fmt);
-
- Packet_Fixed<0x7948> fixed_48;
- fixed_48.account_name = name;
- fixed_48.valid_until = connect_until_time;
- send_fpacket<0x7948, 30>(login_session, fixed_48);
- bytes_to_read = 1;
-}
-
//------------------------------------------------------------------------------
// Sub-function: Asking to displaying information about an account (by its name)
//------------------------------------------------------------------------------
@@ -1874,8 +1593,6 @@ void prompt(void)
infoaccount(wrap<AccountId>(static_cast<uint32_t>(atoi(parameters.c_str()))));
else if (command == "kami"_s)
sendbroadcast(parameters); // flag for normal
- else if (command == "itemfrob"_s)
- itemfrob(parameters); // 0: to list all
else if (command == "list"_s)
listaccount(parameters, 0); // 0: to list all
else if (command == "listban"_s)
@@ -1898,10 +1615,6 @@ void prompt(void)
changesex(parameters);
else if (command == "state"_s)
changestate(parameters);
- else if (command == "timeadd"_s)
- timeaddaccount(parameters);
- else if (command == "timeset"_s)
- timesetaccount(parameters);
else if (command == "unban"_s)
unbanaccount(parameters);
else if (command == "unblock"_s)
@@ -1974,7 +1687,7 @@ void parse_fromlogin(Session *s)
break;
Iprintf(" Login-Server [%s:%d]\n"_fmt,
- login_ip, login_port);
+ admin_conf.login_ip, admin_conf.login_port);
Version version = fixed.version;
Iprintf(" tmwA version %hhu.%hhu.%hhu (dev? %hhu) (flags %hhx) (which %hhx) (vend %hu)\n"_fmt,
version.major, version.minor, version.patch,
@@ -1986,17 +1699,6 @@ void parse_fromlogin(Session *s)
break;
}
- case 0x7925: // Itemfrob-OK
- {
- Packet_Fixed<0x7925> fixed;
- rv = recv_fpacket<0x7925, 2>(s, fixed);
- if (rv != RecvResult::Complete)
- break;
-
- bytes_to_read = 0;
- break;
- }
-
case 0x7921: // Displaying of the list of accounts
{
std::vector<Packet_Repeat<0x7921>> repeat;
@@ -2487,47 +2189,6 @@ void parse_fromlogin(Session *s)
break;
}
- case 0x7949: // answer of an account validity limit set
- {
- Packet_Fixed<0x7949> fixed;
- rv = recv_fpacket<0x7949, 34>(s, fixed);
- if (rv != RecvResult::Complete)
- break;
-
- AccountId account_id = fixed.account_id;
- AccountName name = fixed.account_name;
- if (!account_id)
- {
- PRINTF("Account [%s] validity limit changing failed. Account doesn't exist.\n"_fmt,
- name);
- LADMIN_LOG("Account [%s] validity limit changing failed. Account doesn't exist.\n"_fmt,
- name);
- }
- else
- {
- TimeT timestamp = fixed.valid_until;
- if (!timestamp)
- {
- PRINTF("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n"_fmt,
- name, account_id);
- LADMIN_LOG("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n"_fmt,
- name, account_id);
- }
- else
- {
- timestamp_seconds_buffer tmpstr;
- stamp_time(tmpstr, &timestamp);
- PRINTF("Validity Limit of the account [%s][id: %d] successfully changed to be until %s.\n"_fmt,
- name, account_id, tmpstr);
- LADMIN_LOG("Validity Limit of the account [%s][id: %d] successfully changed to be until %s.\n"_fmt,
- name, account_id,
- tmpstr);
- }
- }
- bytes_to_read = 0;
- break;
- }
-
case 0x794b: // answer of an account ban set
{
Packet_Fixed<0x794b> fixed;
@@ -2632,50 +2293,6 @@ void parse_fromlogin(Session *s)
break;
}
- case 0x7951: // answer of an account validity limit changing
- {
- Packet_Fixed<0x7951> fixed;
- rv = recv_fpacket<0x7951, 34>(s, fixed);
- if (rv != RecvResult::Complete)
- break;
-
- AccountId account_id = fixed.account_id;
- AccountName name = fixed.account_name;
- if (!account_id)
- {
- PRINTF("Account [%s] validity limit changing failed. Account doesn't exist.\n"_fmt,
- name);
- LADMIN_LOG("Account [%s] validity limit changing failed. Account doesn't exist.\n"_fmt,
- name);
- }
- else
- {
- TimeT timestamp = fixed.valid_until;
- if (!timestamp)
- {
- PRINTF("Validity limit of the account [%s][id: %d] unchanged.\n"_fmt,
- name, account_id);
- PRINTF("The account have an unlimited validity limit or\n"_fmt);
- PRINTF("the changing is impossible with the proposed adjustments.\n"_fmt);
- LADMIN_LOG("Validity limit of the account [%s][id: %d] unchanged. The account have an unlimited validity limit or the changing is impossible with the proposed adjustments.\n"_fmt,
- name, account_id);
- }
- else
- {
- timestamp_seconds_buffer tmpstr;
- stamp_time(tmpstr, &timestamp);
- PRINTF("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n"_fmt,
- name, account_id,
- tmpstr);
- LADMIN_LOG("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n"_fmt,
- name, account_id,
- tmpstr);
- }
- }
- bytes_to_read = 0;
- break;
- }
-
case 0x7953: // answer of a request about informations of an account (by account name/id)
{
Packet_Head<0x7953> head;
@@ -2696,7 +2313,6 @@ void parse_fromlogin(Session *s)
timestamp_milliseconds_buffer lastlogin = head.last_login_string;
VString<15> last_ip_ = head.ip_string;
AccountEmail email = head.email;
- TimeT connect_until_time = head.connect_until;
TimeT ban_until_time = head.ban_until;
AString& memo = repeat;
if (!account_id)
@@ -2790,17 +2406,9 @@ void parse_fromlogin(Session *s)
connections);
PRINTF(" Last connection at: %s (ip: %s)\n"_fmt,
lastlogin, last_ip_);
- if (!connect_until_time)
{
PRINTF(" Validity limit: unlimited.\n"_fmt);
}
- else
- {
- timestamp_seconds_buffer tmpstr;
- stamp_time(tmpstr, &connect_until_time);
- PRINTF(" Validity limit: until %s.\n"_fmt,
- tmpstr);
- }
PRINTF(" Memo: '%s'\n"_fmt, memo);
}
}
@@ -2836,12 +2444,12 @@ void parse_fromlogin(Session *s)
// Function to connect to login-server
//------------------------------------
static
-int Connect_login_server(void)
+int connect_login_server(void)
{
Iprintf("Attempt to connect to login-server...\n"_fmt);
LADMIN_LOG("Attempt to connect to login-server...\n"_fmt);
- login_session = make_connection(login_ip, login_port, SessionParsers{.func_parse= parse_fromlogin, .func_delete= delete_fromlogin});
+ login_session = make_connection(admin_conf.login_ip, admin_conf.login_port, SessionParsers{.func_parse= parse_fromlogin, .func_delete= delete_fromlogin});
if (!login_session)
return 0;
@@ -2849,7 +2457,7 @@ int Connect_login_server(void)
{
Packet_Fixed<0x7918> fixed_18;
fixed_18.encryption_zero = 0;
- fixed_18.account_pass = admin_pass;
+ fixed_18.account_pass = admin_conf.admin_pass;
send_fpacket<0x7918, 28>(login_session, fixed_18);
bytes_to_read = 1;
@@ -2861,59 +2469,36 @@ int Connect_login_server(void)
}
static
-bool admin_confs(XString w1, ZString w2)
+bool admin_config(io::Spanned<XString> key, io::Spanned<ZString> value)
{
+ return parse_admin_conf(admin_conf, key, value);
+}
+
+static
+bool admin_confs(io::Spanned<XString> key, io::Spanned<ZString> value)
+{
+ if (key.data == "admin_conf"_s)
{
- if (w1 == "login_ip"_s)
- {
- struct hostent *h = gethostbyname(w2.c_str());
- if (h != nullptr)
- {
- Iprintf("Login server IP address: %s -> %s\n"_fmt,
- w2, login_ip);
- login_ip = IP4Address({
- static_cast<uint8_t>(h->h_addr[0]),
- static_cast<uint8_t>(h->h_addr[1]),
- static_cast<uint8_t>(h->h_addr[2]),
- static_cast<uint8_t>(h->h_addr[3]),
- });
- }
- }
- else if (w1 == "login_port"_s)
- {
- login_port = atoi(w2.c_str());
- }
- else if (w1 == "admin_pass"_s)
- {
- admin_pass = stringish<AccountPass>(w2);
- }
- else if (w1 == "ladmin_log_filename"_s)
- {
- ladmin_log_filename = w2;
- }
- else
- {
- PRINTF("WARNING: unknown ladmin config key: %s\n"_fmt, AString(w1));
- return false;
- }
+ return load_config_file(value.data, admin_config);
}
- return true;
+ key.span.error("Unknown meta-key for admin nonserver"_s);
+ return false;
}
+} // namespace admin
//--------------------------------------
// Function called at exit of the server
//--------------------------------------
void term_func(void)
{
-
- if (already_exit_function == 0)
+ if (admin::already_exit_function == 0)
{
- delete_session(login_session);
+ delete_session(admin::login_session);
Iprintf(SGR_RESET "----End of Ladmin (normal end with closing of all files).\n"_fmt);
LADMIN_LOG("----End of Ladmin (normal end with closing of all files).\n"_fmt);
- already_exit_function = 1;
+ admin::already_exit_function = 1;
}
}
@@ -2949,14 +2534,14 @@ int do_init(Slice<ZString> argv)
else
{
loaded_config_yet = true;
- runflag &= load_config_file(argvi, admin_confs);
+ runflag &= load_config_file(argvi, admin::admin_confs);
}
}
if (!loaded_config_yet)
- runflag &= load_config_file("conf/tmwa-admin.conf"_s, admin_confs);
+ runflag &= load_config_file("conf/tmwa-admin.conf"_s, admin::admin_confs);
- eathena_interactive_session = isatty(0);
+ admin::eathena_interactive_session = isatty(0);
LADMIN_LOG(""_fmt);
LADMIN_LOG("Configuration file readed.\n"_fmt);
@@ -2973,8 +2558,9 @@ int do_init(Slice<ZString> argv)
LADMIN_LOG("Ladmin is ready.\n"_fmt);
Iprintf("Ladmin is " SGR_BOLD SGR_GREEN "ready" SGR_RESET ".\n\n"_fmt);
- Connect_login_server();
+ admin::connect_login_server();
return 0;
}
+// namespace admin ends before term_func and do_init
} // namespace tmwa
diff --git a/src/admin/main.cpp b/src/admin/main.cpp
index 1849d80..babf195 100644
--- a/src/admin/main.cpp
+++ b/src/admin/main.cpp
@@ -17,7 +17,7 @@
// 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 "../mmo/core.hpp"
+#include "../high/core.hpp"
#include "ladmin.hpp"
diff --git a/src/ast/fwd.hpp b/src/ast/fwd.hpp
new file mode 100644
index 0000000..24bf545
--- /dev/null
+++ b/src/ast/fwd.hpp
@@ -0,0 +1,45 @@
+#pragma once
+// ast/fwd.hpp - list of type names for new independent tmwa ast
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "../sanity.hpp"
+
+#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
new file mode 100644
index 0000000..bd4f295
--- /dev/null
+++ b/src/ast/item.cpp
@@ -0,0 +1,155 @@
+#include "item.hpp"
+// ast/item.cpp - Structure of itemdb
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "../io/extract.hpp"
+#include "../io/line.hpp"
+
+#include "../mmo/extract_enums.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace ast
+{
+namespace item
+{
+ using io::respan;
+
+ static
+ void skip_comma_space(io::LineCharReader& lr)
+ {
+ io::LineChar c;
+ if (lr.get(c) && c.ch() == ',')
+ {
+ lr.adv();
+ while (lr.get(c) && c.ch() == ' ')
+ {
+ lr.adv();
+ }
+ }
+ }
+ static
+ Option<Spanned<RString>> lex_nonscript(io::LineCharReader& lr, bool first)
+ {
+ io::LineChar c;
+ if (first)
+ {
+ while (lr.get(c) && c.ch() == '\n')
+ {
+ lr.adv();
+ }
+ }
+ if (!lr.get(c))
+ {
+ return None;
+ }
+ io::LineSpan span;
+ MString accum;
+ accum += c.ch();
+ span.begin = c;
+ span.end = c;
+ lr.adv();
+ if (c.ch() != '/')
+ first = false;
+
+ if (first && lr.get(c) && c.ch() == '/')
+ {
+ accum += c.ch();
+ span.end = c;
+ lr.adv();
+ while (lr.get(c) && c.ch() != '\n')
+ {
+ accum += c.ch();
+ span.end = c;
+ lr.adv();
+ }
+ return Some(respan(span, RString(accum)));
+ }
+
+ while (lr.get(c) && c.ch() != ',' && c.ch() != '\n')
+ {
+ accum += c.ch();
+ span.end = c;
+ lr.adv();
+ }
+ skip_comma_space(lr);
+ return Some(respan(span, RString(accum)));
+ }
+
+ static
+ Result<ast::script::ScriptBody> lex_script(io::LineCharReader& lr)
+ {
+ ast::script::ScriptOptions opt;
+ 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())
+ {
+ skip_comma_space(lr);
+ }
+ 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 EOL_ERROR(lr) ({ io::LineChar c; lr.get(c) ? Err(c.error_str("unexpected EOL"_s)) : Err("unexpected EOF before unexpected EOL"_s); })
+ Option<Result<ItemOrComment>> parse_item(io::LineCharReader& lr)
+ {
+ Spanned<RString> first = TRY_UNWRAP(lex_nonscript(lr, true), return None);
+ if (first.data.startswith("//"_s))
+ {
+ Comment comment;
+ comment.comment = first.data;
+ ItemOrComment rv = std::move(comment);
+ rv.span = first.span;
+ return Some(Ok(std::move(rv)));
+ }
+ Item item;
+ SPAN_EXTRACT(first, item.id);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.name);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.jname);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.type);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.buy_price);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.sell_price);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.weight);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.atk);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.def);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.range);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.magic_bonus);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.slot_unused);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.gender);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.loc);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.wlv);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.elv);
+ 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));
+ 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
+} // namespace tmwa
diff --git a/src/ast/item.hpp b/src/ast/item.hpp
new file mode 100644
index 0000000..a8fe908
--- /dev/null
+++ b/src/ast/item.hpp
@@ -0,0 +1,82 @@
+#pragma once
+// ast/item.hpp - Structure of tmwa itemdb
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#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"
+
+#include "script.hpp"
+
+
+namespace tmwa
+{
+namespace ast
+{
+namespace item
+{
+ using io::Spanned;
+
+ struct Comment
+ {
+ RString comment;
+ };
+ struct Item
+ {
+ Spanned<ItemNameId> id;
+ Spanned<ItemName> name;
+ Spanned<ItemName> jname;
+ Spanned<ItemType> type;
+ Spanned<int> buy_price;
+ Spanned<int> sell_price;
+ Spanned<int> weight;
+ Spanned<int> atk;
+ Spanned<int> def;
+ Spanned<int> range;
+ Spanned<int> magic_bonus;
+ Spanned<RString> slot_unused;
+ Spanned<SEX> gender;
+ Spanned<EPOS> loc;
+ Spanned<int> wlv;
+ Spanned<int> elv;
+ Spanned<ItemLook> view;
+ ast::script::ScriptBody use_script;
+ ast::script::ScriptBody equip_script;
+ };
+
+ 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
new file mode 100644
index 0000000..a77662e
--- /dev/null
+++ b/src/ast/item_test.cpp
@@ -0,0 +1,150 @@
+#include "item.hpp"
+// ast/item_test.cpp - Testsuite for itemdb parser.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 <gtest/gtest.h>
+
+#include "../io/line.hpp"
+
+#include "../tests/fdhack.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace ast
+{
+namespace item
+{
+#define EXPECT_SPAN(span, bl,bc, el,ec) \
+ ({ \
+ EXPECT_EQ((span).begin.line, bl); \
+ EXPECT_EQ((span).begin.column, bc); \
+ EXPECT_EQ((span).end.line, el); \
+ EXPECT_EQ((span).end.column, ec); \
+ })
+
+ TEST(itemast, eof)
+ {
+ QuietFd q;
+ LString inputs[] =
+ {
+ ""_s,
+ "\n"_s,
+ "\n\n"_s,
+ };
+ for (auto input : inputs)
+ {
+ io::LineCharReader lr(io::from_string, "<string>"_s, input);
+ auto res = parse_item(lr);
+ EXPECT_TRUE(res.is_none());
+ }
+ }
+ TEST(itemast, comment)
+ {
+ QuietFd q;
+ LString inputs[] =
+ {
+ //23456789
+ "// hello"_s,
+ "// hello\n "_s,
+ "// hello\nabc"_s,
+ };
+ for (auto input : inputs)
+ {
+ io::LineCharReader lr(io::from_string, "<string>"_s, input);
+ 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 = top.get_if<Comment>();
+ EXPECT_TRUE(p);
+ if (p)
+ {
+ EXPECT_EQ(p->comment, "// hello"_s);
+ }
+ }
+ }
+ TEST(itemast, item)
+ {
+ QuietFd q;
+ LString inputs[] =
+ {
+ // 1 2 3 4 5
+ //2345678901234567890123456789012345678901234567890123456789
+ "1,abc , def,3,4,5,6,7,8,9,10,xx,2,16,12,13,11, {end;}, {}"_s,
+ "1,abc , def,3,4,5,6,7,8,9,10,xx,2,16,12,13,11, {end;}, {}\n"_s,
+ "1,abc , def,3,4,5,6,7,8,9,10,xx,2,16,12,13,11, {end;}, {}\nabc"_s,
+ };
+ for (auto input : inputs)
+ {
+ io::LineCharReader lr(io::from_string, "<string>"_s, input);
+ 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 = top.get_if<Item>();
+ EXPECT_TRUE(p);
+ if (p)
+ {
+ EXPECT_SPAN(p->id.span, 1,1, 1,1);
+ EXPECT_EQ(p->id.data, wrap<ItemNameId>(1));
+ EXPECT_SPAN(p->name.span, 1,3, 1,6);
+ EXPECT_EQ(p->name.data, stringish<ItemName>("abc "_s));
+ EXPECT_SPAN(p->jname.span, 1,10, 1,12);
+ EXPECT_EQ(p->jname.data, stringish<ItemName>("def"_s));
+ EXPECT_SPAN(p->type.span, 1,14, 1,14);
+ EXPECT_EQ(p->type.data, ItemType::JUNK);
+ EXPECT_SPAN(p->buy_price.span, 1,16, 1,16);
+ EXPECT_EQ(p->buy_price.data, 4);
+ EXPECT_SPAN(p->sell_price.span, 1,18, 1,18);
+ EXPECT_EQ(p->sell_price.data, 5);
+ EXPECT_SPAN(p->weight.span, 1,20, 1,20);
+ EXPECT_EQ(p->weight.data, 6);
+ EXPECT_SPAN(p->atk.span, 1,22, 1,22);
+ EXPECT_EQ(p->atk.data, 7);
+ EXPECT_SPAN(p->def.span, 1,24, 1,24);
+ EXPECT_EQ(p->def.data, 8);
+ EXPECT_SPAN(p->range.span, 1,26, 1,26);
+ EXPECT_EQ(p->range.data, 9);
+ EXPECT_SPAN(p->magic_bonus.span, 1,28, 1,29);
+ EXPECT_EQ(p->magic_bonus.data, 10);
+ EXPECT_SPAN(p->slot_unused.span, 1,31, 1,32);
+ EXPECT_EQ(p->slot_unused.data, "xx"_s);
+ EXPECT_SPAN(p->gender.span, 1,34, 1,34);
+ EXPECT_EQ(p->gender.data, SEX::NEUTRAL);
+ EXPECT_SPAN(p->loc.span, 1,36, 1,37);
+ EXPECT_EQ(p->loc.data, EPOS::MISC1);
+ EXPECT_SPAN(p->wlv.span, 1,39, 1,40);
+ EXPECT_EQ(p->wlv.data, 12);
+ EXPECT_SPAN(p->elv.span, 1,42, 1,43);
+ EXPECT_EQ(p->elv.data, 13);
+ EXPECT_SPAN(p->view.span, 1,45, 1,46);
+ EXPECT_EQ(p->view.data, ItemLook::BOW);
+ EXPECT_SPAN(p->use_script.span, 1,49, 1,54);
+ EXPECT_EQ(p->use_script.braced_body, "{end;}"_s);
+ EXPECT_SPAN(p->equip_script.span, 1,57, 1,58);
+ EXPECT_EQ(p->equip_script.braced_body, "{}"_s);
+ }
+ }
+ }
+} // namespace item
+} // namespace ast
+} // namespace tmwa
diff --git a/src/ast/npc.cpp b/src/ast/npc.cpp
new file mode 100644
index 0000000..8d4a43e
--- /dev/null
+++ b/src/ast/npc.cpp
@@ -0,0 +1,620 @@
+#include "npc.hpp"
+// ast/npc.cpp - Structure of non-player characters (including mobs).
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
+#include "../io/line.hpp"
+
+#include "../mmo/extract_enums.hpp"
+
+#include "../high/extract_mmo.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace ast
+{
+namespace npc
+{
+ using io::respan;
+
+#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)
+ {
+ if (bits.size() != 4)
+ {
+ return Err(span.error_str("expect 4 |component|s"_s));
+ }
+ if (bits[0].data.size() != 3)
+ {
+ 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("in |component 3| expect 1 ,component,s"_s));
+ }
+ if (bits[3].data.size() != 5)
+ {
+ return Err(bits[3].span.error_str("in |component 4| expect 5 ,component,s"_s));
+ }
+
+ Warp warp;
+ 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);
+ return Ok(std::move(warp));
+ }
+ static
+ Result<Shop> parse_shop(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
+ {
+ if (bits.size() != 4)
+ {
+ return Err(span.error_str("expect 4 |component|s"_s));
+ }
+ if (bits[0].data.size() != 4)
+ {
+ 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("in |component 3| expect 1 ,component,s"_s));
+ }
+ if (bits[3].data.size() < 2)
+ {
+ return Err(bits[3].span.error_str("in |component 4| expect at least 2 ,component,s"_s));
+ }
+
+ Shop shop;
+ TRY_EXTRACT(bits[0].data[0], shop.m);
+ TRY_EXTRACT(bits[0].data[1], shop.x);
+ TRY_EXTRACT(bits[0].data[2], shop.y);
+ TRY_EXTRACT(bits[0].data[3], shop.d);
+ shop.key_span = bits[1].data[0].span;
+ TRY_EXTRACT(bits[2].data[0], shop.name);
+ TRY_EXTRACT(bits[3].data[0], shop.npc_class);
+ shop.items.span = bits[3].span;
+ shop.items.span.begin = bits[3].data[1].span.begin;
+ for (size_t i = 1; i < bits[3].data.size(); ++i)
+ {
+ shop.items.data.emplace_back();
+ auto& item = shop.items.data.back();
+ auto& data = bits[3].data[i];
+ assert(data.span.begin.line == data.span.end.line);
+ item.span = data.span;
+
+ 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;
+ item.data.value.span = item.span;
+ item.data.value.span.begin.column = item.data.name.span.begin.column + name.size() + 1;
+ if (name.endswith(' '))
+ {
+ item.data.name.span.warning("Shop item has useless space before the colon"_s);
+ name = name.rstrip();
+ }
+ if (name.is_digit10())
+ {
+ 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.data.name.span.error_str("item name problem (too long?)"_s));
+ }
+ item.data.value_multiply = false;
+ if (value.startswith('-'))
+ {
+ item.data.value.span.begin.warning("Shop value multiplier should use '*' instead of '-' now"_s);
+ item.data.value_multiply = true;
+ item.data.value.span.begin.column += 1;
+ value = value.xslice_t(1);
+ }
+ else if (value.startswith('*'))
+ {
+ item.data.value_multiply = true;
+ item.data.value.span.begin.column += 1;
+ 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));
+ }
+ static
+ Result<Monster> parse_monster(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
+ {
+ if (bits.size() != 4)
+ {
+ return Err(span.error_str("expect 4 |component|s"_s));
+ }
+ if (bits[0].data.size() != 3 && bits[0].data.size() != 5)
+ {
+ 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("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("in |component 4| expect 2, 4, or 5 ,component,s"_s));
+ }
+
+ Monster mob;
+ TRY_EXTRACT(bits[0].data[0], mob.m);
+ TRY_EXTRACT(bits[0].data[1], mob.x);
+ TRY_EXTRACT(bits[0].data[2], mob.y);
+ if (bits[0].data.size() >= 5)
+ {
+ TRY_EXTRACT(bits[0].data[3], mob.xs);
+ TRY_EXTRACT(bits[0].data[4], mob.ys);
+ }
+ else
+ {
+ mob.xs.data = 0;
+ mob.xs.span = bits[0].data[2].span;
+ mob.xs.span.end.column++;
+ mob.xs.span.begin.column = mob.xs.span.end.column;
+ mob.ys.data = 0;
+ mob.ys.span = mob.xs.span;
+ }
+ mob.key_span = bits[1].data[0].span;
+ TRY_EXTRACT(bits[2].data[0], mob.name);
+ TRY_EXTRACT(bits[3].data[0], mob.mob_class);
+ TRY_EXTRACT(bits[3].data[1], mob.num);
+ if (bits[3].data.size() >= 4)
+ {
+ static_assert(std::is_same<decltype(mob.delay1.data)::period, std::chrono::milliseconds::period>::value, "delay1 is milliseconds");
+ static_assert(std::is_same<decltype(mob.delay2.data)::period, std::chrono::milliseconds::period>::value, "delay2 is milliseconds");
+ if (bits[3].data[2].data.is_digit10())
+ bits[3].data[2].span.warning("delay1 lacks units; defaulting to ms"_s);
+ if (bits[3].data[3].data.is_digit10())
+ bits[3].data[3].span.warning("delay2 lacks units; defaulting to ms"_s);
+ TRY_EXTRACT(bits[3].data[2], mob.delay1);
+ TRY_EXTRACT(bits[3].data[3], mob.delay2);
+ if (bits[3].data.size() >= 5)
+ {
+ TRY_EXTRACT(bits[3].data[4], mob.event);
+ }
+ else
+ {
+ mob.event.data = NpcEvent();
+ mob.event.span = bits[3].data[3].span;
+ mob.event.span.end.column++;
+ mob.event.span.begin.column = mob.event.span.end.column;
+ }
+ }
+ else
+ {
+ mob.delay1.data = std::chrono::milliseconds::zero();
+ mob.delay1.span = bits[3].data[1].span;
+ mob.delay1.span.end.column++;
+ mob.delay1.span.begin.column = mob.delay1.span.end.column;
+ mob.delay2.data = std::chrono::milliseconds::zero();
+ mob.delay2.span = mob.delay1.span;
+ mob.event.data = NpcEvent();
+ mob.event.span = mob.delay1.span;
+ }
+ return Ok(std::move(mob));
+ }
+ static
+ Result<MapFlag> parse_mapflag(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
+ {
+ if (bits.size() != 3 && bits.size() != 4)
+ {
+ return Err(span.error_str("expect 3 or 4 |component|s"_s));
+ }
+ if (bits[0].data.size() != 1)
+ {
+ 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("in |component 3| expect 1 ,component,s"_s));
+ }
+
+ MapFlag mapflag;
+ 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)
+ {
+ 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.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_head(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
+ {
+ // ScriptFunction: function|script|Fun Name{code}
+ if (bits.size() != 3)
+ {
+ return Err(span.error_str("expect 3 |component|s"_s));
+ }
+ assert(bits[0].data.size() == 1);
+ assert(bits[0].data[0].data == "function"_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("in |component 3| expect 1 ,component,s"_s));
+ }
+
+ ScriptFunction script_function;
+ script_function.key1_span = bits[0].data[0].span;
+ TRY_EXTRACT(bits[2].data[0], script_function.name);
+ // also expect '{' and parse real script (in caller)
+ return Ok(std::move(script_function));
+ }
+ static
+ Result<ScriptNone> parse_script_none_head(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
+ {
+ // ScriptNone: -|script|script name|32767{code}
+ if (bits.size() != 4)
+ {
+ return Err(span.error_str("expect 4 |component|s"_s));
+ }
+ assert(bits[0].data.size() == 1);
+ assert(bits[0].data[0].data == "-"_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("in |component 3| expect 1 ,component,s"_s));
+ }
+ assert(bits[3].data[0].data == "32767"_s);
+ if (bits[3].data.size() != 1)
+ {
+ return Err(bits[3].span.error_str("in |component 4| should be just 32767"_s));
+ }
+
+ ScriptNone script_none;
+ script_none.key1_span = bits[0].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 (in caller)
+ return Ok(std::move(script_none));
+ }
+ static
+ 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)
+ {
+ return Err(span.error_str("expect 4 |component|s"_s));
+ }
+ if (bits[0].data.size() != 4)
+ {
+ 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("in |component 3| expect 1 ,component,s"_s));
+ }
+ if (bits[3].data.size() != 1 && bits[3].data.size() != 3)
+ {
+ return Err(bits[3].span.error_str("in |component 4| expect 1 or 3 ,component,s"_s));
+ }
+
+ ScriptMap script_map;
+ 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);
+ TRY_EXTRACT(bits[2].data[0], script_map.name);
+ TRY_EXTRACT(bits[3].data[0], script_map.npc_class);
+ 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<Script> parse_script_any(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
+ {
+ // 3 cases:
+ // ScriptFunction: function|script|Fun Name{code}
+ // ScriptNone: -|script|script name|32767{code}
+ // ScriptMap: m,x,y,d|script|script name|class,xs,ys{code}
+ if (bits[0].data[0].data == "function"_s)
+ {
+ 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)
+ {
+ 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
+ {
+ 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));
+ }
+ }
+
+ /// Try to extract a top-level token
+ /// Return None at EOL, or Some(span)
+ /// This will alternate betweeen returning words and separators
+ static
+ Option<Spanned<RString>> lex(io::LineCharReader& lr, bool first)
+ {
+ // you know, I just realized a lot of the if (.get()) checks are not
+ // actually going to fail, since LineCharReader guarantees the \n
+ // occurs before EOF
+ io::LineChar c;
+ // at start of line, skip whitespace
+ if (first)
+ {
+ while (lr.get(c) && (/*c.ch() == ' ' ||*/ c.ch() == '\n'))
+ {
+ lr.adv();
+ }
+ }
+ // at end of file, end of line, or start of script, signal end
+ if (!lr.get(c) || c.ch() == '\n' || c.ch() == '{')
+ {
+ return None;
+ }
+ // 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;
+ MString accum;
+ accum += c.ch();
+ span.begin = c;
+ span.end = c;
+ lr.adv();
+ if (c.ch() != '/')
+ first = false;
+
+ // if one-char token followed by an end-of-line or separator, stop
+ if (!lr.get(c) || c.ch() == '\n' || c.ch() == '{' || c.ch() == ',' || c.ch() == '|')
+ {
+ return Some(respan(span, RString(accum)));
+ }
+
+ accum += c.ch();
+ span.end = c;
+ lr.adv();
+
+ // if first token on line, can get comment
+ if (first && c.ch() == '/')
+ {
+ while (lr.get(c) && c.ch() != '\n')
+ {
+ accum += c.ch();
+ span.end = c;
+ lr.adv();
+ }
+ return Some(respan(span, RString(accum)));
+ }
+ // otherwise, collect until an end of line or separator
+ while (lr.get(c) && c.ch() != '\n' && c.ch() != '{' && c.ch() != ',' && c.ch() != '|')
+ {
+ accum += c.ch();
+ span.end = c;
+ lr.adv();
+ }
+ return Some(respan(span, RString(accum)));
+ }
+
+ Option<Result<TopLevel>> parse_top(io::LineCharReader& in)
+ {
+ Spanned<std::vector<Spanned<std::vector<Spanned<RString>>>>> bits;
+
+ // special logic for the first 'bit'
+ {
+ Spanned<RString> mayc = TRY_UNWRAP(lex(in, true),
+ {
+ io::LineChar c;
+ if (in.get(c) && c.ch() == '{')
+ {
+ return Err(c.error_str("Unexpected script open"_s));
+ }
+ return None;
+ });
+ if (mayc.data.startswith("//"_s))
+ {
+ Comment com;
+ com.comment = std::move(mayc.data);
+ TopLevel rv = std::move(com);
+ rv.span = std::move(mayc.span);
+ return Some(Ok(std::move(rv)));
+ }
+
+ if (mayc.data == "|"_s || mayc.data == ","_s)
+ return Err(mayc.span.error_str("Unexpected separator"_s));
+ bits.span = mayc.span;
+ bits.data.emplace_back();
+ bits.data.back().span = mayc.span;
+ bits.data.back().data.push_back(mayc);
+ }
+
+ while (true)
+ {
+ Spanned<RString> sep = TRY_UNWRAP(lex(in, false),
+ break);
+ if (sep.data == "|"_s)
+ {
+ bits.data.emplace_back();
+ }
+ else if (sep.data != ","_s)
+ {
+ return Err(sep.span.error_str("Expected separator"_s));
+ }
+
+ Spanned<RString> word = TRY_UNWRAP(lex(in, false),
+ return Err(sep.span.error_str("Expected word after separator"_s)));
+ if (bits.data.back().data.empty())
+ bits.data.back().span = word.span;
+ else
+ bits.data.back().span.end = word.span.end;
+ bits.span.end = word.span.end;
+ bits.data.back().data.push_back(std::move(word));
+ }
+
+ if (bits.data.size() < 2)
+ return Err(bits.span.error_str("Expected a line with |s in it"_s));
+ for (auto& bit : bits.data)
+ {
+ if (bit.data.empty())
+ return Err(bit.span.error_str("Empty components are not cool"_s));
+ }
+ if (bits.data[1].data.size() != 1)
+ return Err(bits.data[1].span.error_str("Expected a single word in type position"_s));
+ Spanned<RString>& w2 = bits.data[1].data[0];
+ if (w2.data == "warp"_s)
+ {
+ 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)
+ {
+ 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)
+ {
+ 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)
+ {
+ 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)
+ {
+ 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));
+ }
+ }
+} // namespace npc
+} // namespace ast
+} // namespace tmwa
diff --git a/src/ast/npc.hpp b/src/ast/npc.hpp
new file mode 100644
index 0000000..ca6479e
--- /dev/null
+++ b/src/ast/npc.hpp
@@ -0,0 +1,142 @@
+#pragma once
+// ast/npc.hpp - Structure of non-player characters (including mobs).
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+#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 "script.hpp"
+
+
+namespace tmwa
+{
+namespace ast
+{
+namespace npc
+{
+ using io::Spanned;
+
+ struct Comment
+ {
+ RString comment;
+ };
+ struct Warp
+ {
+ Spanned<MapName> m;
+ Spanned<unsigned> x, y;
+ io::LineSpan key_span;
+ Spanned<NpcName> name;
+ Spanned<unsigned> xs, ys;
+ Spanned<MapName> to_m;
+ Spanned<unsigned> to_x, to_y;
+ };
+ struct ShopItem
+ {
+ Spanned<ItemName> name;
+ bool value_multiply;
+ Spanned<int> value;
+ };
+ struct Shop
+ {
+ Spanned<MapName> m;
+ Spanned<unsigned> x, y;
+ Spanned<DIR> d;
+ io::LineSpan key_span;
+ Spanned<NpcName> name;
+ Spanned<Species> npc_class;
+ Spanned<std::vector<Spanned<ShopItem>>> items;
+ };
+ struct Monster
+ {
+ Spanned<MapName> m;
+ Spanned<unsigned> x, y;
+ Spanned<unsigned> xs, ys;
+ io::LineSpan key_span;
+ Spanned<MobName> name;
+ Spanned<Species> mob_class;
+ Spanned<unsigned> num;
+ Spanned<interval_t> delay1, delay2;
+ Spanned<NpcEvent> event;
+ };
+ struct MapFlag
+ {
+ Spanned<MapName> m;
+ io::LineSpan key_span;
+ Spanned<RString> name;
+ Spanned<std::vector<Spanned<RString>>> vec_extra;
+ };
+ struct ScriptFunction
+ {
+ io::LineSpan key1_span;
+ Spanned<RString> name;
+ };
+ struct ScriptNone
+ {
+ io::LineSpan key1_span;
+ Spanned<NpcName> name;
+ io::LineSpan key4_span;
+ };
+ struct ScriptMap
+ {
+ Spanned<MapName> m;
+ Spanned<unsigned> x, y;
+ Spanned<DIR> d;
+ Spanned<NpcName> name;
+ Spanned<Species> npc_class;
+ Spanned<unsigned> xs, ys;
+ };
+ using ScriptBase = Variant<ScriptFunction, ScriptNone, ScriptMap>;
+ struct Script : ScriptBase
+ {
+ Script() = default;
+ Script(ScriptFunction s) : ScriptBase(std::move(s)) {}
+ Script(ScriptNone 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;
+ };
+
+ 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
new file mode 100644
index 0000000..dadeba7
--- /dev/null
+++ b/src/ast/npc_test.cpp
@@ -0,0 +1,556 @@
+#include "npc.hpp"
+// ast/npc_test.cpp - Testsuite for npc parser.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 <gtest/gtest.h>
+
+#include "../io/line.hpp"
+
+#include "../tests/fdhack.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace ast
+{
+namespace npc
+{
+#define EXPECT_SPAN(span, bl,bc, el,ec) \
+ ({ \
+ EXPECT_EQ((span).begin.line, bl); \
+ EXPECT_EQ((span).begin.column, bc); \
+ EXPECT_EQ((span).end.line, el); \
+ EXPECT_EQ((span).end.column, ec); \
+ })
+
+ TEST(npcast, eof)
+ {
+ QuietFd q;
+ LString inputs[] =
+ {
+ ""_s,
+ "\n"_s,
+ "\n\n"_s,
+ };
+ for (auto input : inputs)
+ {
+ io::LineCharReader lr(io::from_string, "<string>"_s, input);
+ auto res = parse_top(lr);
+ EXPECT_TRUE(res.is_none());
+ }
+ }
+ TEST(npcast, comment)
+ {
+ QuietFd q;
+ LString inputs[] =
+ {
+ //23456789
+ "// hello"_s,
+ "// hello\n "_s,
+ "// hello\nabc"_s,
+ };
+ for (auto input : inputs)
+ {
+ io::LineCharReader lr(io::from_string, "<string>"_s, input);
+ 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());
+ EXPECT_SPAN(top.span, 1,1, 1,8);
+ auto p = top.get_if<Comment>();
+ EXPECT_TRUE(p);
+ if (p)
+ {
+ EXPECT_EQ(p->comment, "// hello"_s);
+ }
+ }
+ }
+ TEST(npcast, warp)
+ {
+ QuietFd q;
+ LString inputs[] =
+ {
+ // 1 2 3 4
+ //234567890123456789012345678901234567890123456789
+ "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 = TRY_UNWRAP(parse_top(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,47);
+ auto p = top.get_if<Warp>();
+ EXPECT_TRUE(p);
+ if (p)
+ {
+ EXPECT_SPAN(p->m.span, 1,1, 1,7);
+ 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->key_span, 1,13, 1,16);
+ 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, 5);
+ EXPECT_SPAN(p->ys.span, 1,33, 1,33);
+ 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, 7);
+ EXPECT_SPAN(p->to_y.span, 1,47, 1,47);
+ EXPECT_EQ(p->to_y.data, 8);
+ }
+ }
+ }
+ TEST(npcast, shop)
+ {
+ QuietFd q;
+ LString inputs[] =
+ {
+ // 1 2 3 4 5
+ //2345678901234567890123456789012345678901234567890123456789
+ "map.gat,1,2,3|shop|Flower Shop|4,5:6,Named:7,Spaced :*8"_s,
+ "map.gat,1,2,3|shop|Flower Shop|4,5:6,Named:7,Spaced :*8\n"_s,
+ "map.gat,1,2,3|shop|Flower Shop|4,5:6,Named:7,Spaced :*8{"_s,
+ // no optional fields in shop
+ };
+ for (auto input : inputs)
+ {
+ io::LineCharReader lr(io::from_string, "<string>"_s, input);
+ 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());
+ EXPECT_SPAN(top.span, 1,1, 1,55);
+ auto p = top.get_if<Shop>();
+ EXPECT_TRUE(p);
+ if (p)
+ {
+ EXPECT_SPAN(p->m.span, 1,1, 1,7);
+ 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,18);
+ EXPECT_SPAN(p->name.span, 1,20, 1,30);
+ EXPECT_EQ(p->name.data, stringish<NpcName>("Flower Shop"_s));
+ EXPECT_SPAN(p->npc_class.span, 1,32, 1,32);
+ EXPECT_EQ(p->npc_class.data, wrap<Species>(4));
+ EXPECT_SPAN(p->items.span, 1,34, 1,55);
+ EXPECT_EQ(p->items.data.size(), 3);
+ EXPECT_SPAN(p->items.data[0].span, 1,34, 1,36);
+ EXPECT_SPAN(p->items.data[0].data.name.span, 1,34, 1,34);
+ EXPECT_EQ(p->items.data[0].data.name.data, stringish<ItemName>("5"_s));
+ EXPECT_EQ(p->items.data[0].data.value_multiply, false);
+ EXPECT_SPAN(p->items.data[0].data.value.span, 1,36, 1,36);
+ EXPECT_EQ(p->items.data[0].data.value.data, 6);
+ EXPECT_SPAN(p->items.data[1].span, 1,38, 1,44);
+ EXPECT_SPAN(p->items.data[1].data.name.span, 1,38, 1,42);
+ EXPECT_EQ(p->items.data[1].data.name.data, stringish<ItemName>("Named"_s));
+ EXPECT_EQ(p->items.data[1].data.value_multiply, false);
+ EXPECT_SPAN(p->items.data[1].data.value.span, 1,44, 1,44);
+ EXPECT_EQ(p->items.data[1].data.value.data, 7);
+ EXPECT_SPAN(p->items.data[2].span, 1,46, 1,55);
+ EXPECT_SPAN(p->items.data[2].data.name.span, 1,46, 1,52);
+ EXPECT_EQ(p->items.data[2].data.name.data, stringish<ItemName>("Spaced"_s));
+ EXPECT_EQ(p->items.data[2].data.value_multiply, true);
+ EXPECT_SPAN(p->items.data[2].data.value.span, 1,55, 1,55);
+ EXPECT_EQ(p->items.data[2].data.value.data, 8);
+ }
+ }
+ }
+ TEST(npcast, monster)
+ {
+ QuietFd q;
+ LString inputs[] =
+ {
+ // 1 2 3 4 5 6
+ //23456789012345678901234567890123456789012345678901234567890123456789
+ "map.gat,1,2,3,4|monster|Feeping Creature|5,6,7000,8000,Npc::Event"_s,
+ "map.gat,1,2,3,4|monster|Feeping Creature|5,6,7000,8000,Npc::Event\n"_s,
+ "map.gat,1,2,3,4|monster|Feeping Creature|5,6,7000,8000,Npc::Event{"_s,
+ "Map.gat,1,2,3,4|monster|Feeping Creature|5,6,7000,8000"_s,
+ "Map.gat,1,2,3,4|monster|Feeping Creature|5,6,7000,8000\n"_s,
+ "Map.gat,1,2,3,4|monster|Feeping Creature|5,6,7000,8000{"_s,
+ "nap.gat,1,20304|monster|Feeping Creature|506,700008000"_s,
+ "nap.gat,1,20304|monster|Feeping Creature|506,700008000\n"_s,
+ "nap.gat,1,20304|monster|Feeping Creature|506,700008000{"_s,
+ };
+ for (auto input : inputs)
+ {
+ bool first = input.startswith('m');
+ bool second = input.startswith('M');
+ bool third = input.startswith('n');
+ assert(first + second + third == 1);
+ io::LineCharReader lr(io::from_string, "<string>"_s, input);
+ 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());
+ EXPECT_SPAN(top.span, 1,1, 1,first?65:54);
+ auto p = top.get_if<Monster>();
+ EXPECT_TRUE(p);
+ if (p)
+ {
+ EXPECT_SPAN(p->m.span, 1,1, 1,7);
+ if (first)
+ {
+ EXPECT_EQ(p->m.data, stringish<MapName>("map"_s));
+ }
+ else if (second)
+ {
+ EXPECT_EQ(p->m.data, stringish<MapName>("Map"_s));
+ }
+ else
+ {
+ EXPECT_EQ(p->m.data, stringish<MapName>("nap"_s));
+ }
+ EXPECT_SPAN(p->x.span, 1,9, 1,9);
+ EXPECT_EQ(p->x.data, 1);
+ if (!third)
+ {
+ EXPECT_SPAN(p->y.span, 1,11, 1,11);
+ EXPECT_EQ(p->y.data, 2);
+ EXPECT_SPAN(p->xs.span, 1,13, 1,13);
+ EXPECT_EQ(p->xs.data, 3);
+ EXPECT_SPAN(p->ys.span, 1,15, 1,15);
+ EXPECT_EQ(p->ys.data, 4);
+ }
+ else
+ {
+ EXPECT_SPAN(p->y.span, 1,11, 1,15);
+ EXPECT_EQ(p->y.data, 20304);
+ EXPECT_SPAN(p->xs.span, 1,16, 1,16);
+ EXPECT_EQ(p->xs.data, 0);
+ EXPECT_SPAN(p->ys.span, 1,16, 1,16);
+ EXPECT_EQ(p->ys.data, 0);
+ }
+ EXPECT_SPAN(p->key_span, 1,17, 1,23);
+ EXPECT_SPAN(p->name.span, 1,25, 1,40);
+ EXPECT_EQ(p->name.data, stringish<MobName>("Feeping Creature"_s));
+ if (!third)
+ {
+ EXPECT_SPAN(p->mob_class.span, 1,42, 1,42);
+ EXPECT_EQ(p->mob_class.data, wrap<Species>(5));
+ EXPECT_SPAN(p->num.span, 1,44, 1,44);
+ EXPECT_EQ(p->num.data, 6);
+ EXPECT_SPAN(p->delay1.span, 1,46, 1,49);
+ EXPECT_EQ(p->delay1.data, 7_s);
+ EXPECT_SPAN(p->delay2.span, 1,51, 1,54);
+ EXPECT_EQ(p->delay2.data, 8_s);
+ }
+ else
+ {
+ EXPECT_SPAN(p->mob_class.span, 1,42, 1,44);
+ EXPECT_EQ(p->mob_class.data, wrap<Species>(506));
+ EXPECT_SPAN(p->num.span, 1,46, 1,54);
+ EXPECT_EQ(p->num.data, 700008000);
+ EXPECT_SPAN(p->delay1.span, 1,55, 1,55);
+ EXPECT_EQ(p->delay1.data, 0_s);
+ EXPECT_SPAN(p->delay2.span, 1,55, 1,55);
+ EXPECT_EQ(p->delay2.data, 0_s);
+ }
+ if (first)
+ {
+ EXPECT_SPAN(p->event.span, 1,56, 1,65);
+ EXPECT_EQ(p->event.data.npc, stringish<NpcName>("Npc"_s));
+ EXPECT_EQ(p->event.data.label, stringish<ScriptLabel>("Event"_s));
+ }
+ else
+ {
+ EXPECT_SPAN(p->event.span, 1,55, 1,55);
+ EXPECT_EQ(p->event.data.npc, NpcName());
+ EXPECT_EQ(p->event.data.label, ScriptLabel());
+ }
+ }
+ }
+ }
+ TEST(npcast, mapflag)
+ {
+ QuietFd q;
+ LString inputs[] =
+ {
+ // 1 2 3
+ //23456789012345678901234567890123456789
+ "map.gat|mapflag|flagname"_s,
+ "map.gat|mapflag|flagname\n"_s,
+ "map.gat|mapflag|flagname{"_s,
+ "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 = TRY_UNWRAP(parse_top(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,first?24:31);
+ auto p = top.get_if<MapFlag>();
+ EXPECT_TRUE(p);
+ if (p)
+ {
+ EXPECT_SPAN(p->m.span, 1,1, 1,7);
+ if (first)
+ {
+ EXPECT_EQ(p->m.data, stringish<MapName>("map"_s));
+ }
+ 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 (first)
+ {
+ EXPECT_SPAN(p->vec_extra.span, 1,25, 1,25);
+ EXPECT_EQ(p->vec_extra.data.size(), 0);
+ }
+ if (second)
+ {
+ 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);
+ }
+ }
+ }
+ }
+
+ TEST(npcast, scriptfun)
+ {
+ QuietFd q;
+ LString inputs[] =
+ {
+ // 1 2 3
+ //23456789012345678901234567890123456789
+ "function|script|Fun Name{end;}"_s,
+ // 123456
+ "function|script|Fun Name\n{end;}\n"_s,
+ // 1234567
+ "function|script|Fun Name\n \n {end;} "_s,
+ };
+ for (auto input : inputs)
+ {
+ io::LineCharReader lr(io::from_string, "<string>"_s, input);
+ 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());
+ 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(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(script->body.span, 1,25, 1,30);
+ }
+ else if (input.endswith('\n'))
+ {
+ EXPECT_SPAN(script->body.span, 2,1, 2,6);
+ }
+ else if (input.endswith(' '))
+ {
+ EXPECT_SPAN(script->body.span, 3,2, 3,7);
+ }
+ else
+ {
+ FAIL();
+ }
+ EXPECT_EQ(script->body.braced_body, "{end;}"_s);
+ }
+ }
+ }
+ TEST(npcast, scriptnone)
+ {
+ QuietFd q;
+ LString inputs[] =
+ {
+ // 1 2 3
+ //23456789012345678901234567890123456789
+ "-|script|#config|32767{end;}"_s,
+ // 123456
+ "-|script|#config|32767\n{end;}\n"_s,
+ // 1234567
+ "-|script|#config|32767\n \n {end;} "_s,
+ };
+ for (auto input : inputs)
+ {
+ io::LineCharReader lr(io::from_string, "<string>"_s, input);
+ 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());
+ EXPECT_SPAN(top.span, 1,1, 1,22);
+ 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(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,22);
+ if (input.endswith('}'))
+ {
+ EXPECT_SPAN(script->body.span, 1,23, 1,28);
+ }
+ else if (input.endswith('\n'))
+ {
+ EXPECT_SPAN(script->body.span, 2,1, 2,6);
+ }
+ else if (input.endswith(' '))
+ {
+ EXPECT_SPAN(script->body.span, 3,2, 3,7);
+ }
+ else
+ {
+ FAIL();
+ }
+ EXPECT_EQ(script->body.braced_body, "{end;}"_s);
+ }
+ }
+ }
+ TEST(npcast, scriptmap)
+ {
+ QuietFd q;
+ LString inputs[] =
+ {
+ // 1 2 3
+ //23456789012345678901234567890123456789
+ "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 = TRY_UNWRAP(parse_top(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,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);
+ 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(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));
+ 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(script->body.span, 1,32, 1,37);
+ }
+ else if (input.endswith('\n'))
+ {
+ EXPECT_SPAN(script->body.span, 2,1, 2,6);
+ }
+ else if (input.endswith(' '))
+ {
+ EXPECT_SPAN(script->body.span, 3,2, 3,7);
+ }
+ else
+ {
+ FAIL();
+ }
+ EXPECT_EQ(script->body.braced_body, "{end;}"_s);
+ }
+ }
+ }
+} // namespace npc
+} // namespace ast
+} // namespace tmwa
diff --git a/src/ast/quest.cpp b/src/ast/quest.cpp
new file mode 100644
index 0000000..bd339c2
--- /dev/null
+++ b/src/ast/quest.cpp
@@ -0,0 +1,125 @@
+#include "quest.hpp"
+// ast/quest.cpp - Structure of tmwa questdb
+//
+// Copyright © 2015 Ed Pasek <pasekei@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "../io/extract.hpp"
+#include "../io/line.hpp"
+
+#include "../mmo/extract_enums.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace ast
+{
+namespace quest
+{
+ using io::respan;
+
+ static
+ void skip_comma_space(io::LineCharReader& lr)
+ {
+ io::LineChar c;
+ if (lr.get(c) && c.ch() == ',')
+ {
+ lr.adv();
+ while (lr.get(c) && c.ch() == ' ')
+ {
+ lr.adv();
+ }
+ }
+ }
+ static
+ Option<Spanned<RString>> lex_nonscript(io::LineCharReader& lr, bool first)
+ {
+ io::LineChar c;
+ if (first)
+ {
+ while (lr.get(c) && c.ch() == '\n')
+ {
+ lr.adv();
+ }
+ }
+ if (!lr.get(c))
+ {
+ return None;
+ }
+ io::LineSpan span;
+ MString accum;
+ accum += c.ch();
+ span.begin = c;
+ span.end = c;
+ lr.adv();
+ if (c.ch() != '/')
+ first = false;
+
+ if (first && lr.get(c) && c.ch() == '/')
+ {
+ accum += c.ch();
+ span.end = c;
+ lr.adv();
+ while (lr.get(c) && c.ch() != '\n')
+ {
+ accum += c.ch();
+ span.end = c;
+ lr.adv();
+ }
+ return Some(respan(span, RString(accum)));
+ }
+
+ while (lr.get(c) && c.ch() != ',' && c.ch() != '\n')
+ {
+ accum += c.ch();
+ span.end = c;
+ lr.adv();
+ }
+ skip_comma_space(lr);
+ return Some(respan(span, RString(accum)));
+ }
+
+#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); })
+ Option<Result<QuestOrComment>> parse_quest(io::LineCharReader& lr)
+ {
+ Spanned<RString> first = TRY_UNWRAP(lex_nonscript(lr, true), return None);
+ if (first.data.startswith("//"_s))
+ {
+ Comment comment;
+ comment.comment = first.data;
+ QuestOrComment rv = std::move(comment);
+ rv.span = first.span;
+ return Some(Ok(std::move(rv)));
+ }
+ Quest quest;
+ SPAN_EXTRACT(first, quest.questid);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), quest.quest_var);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), quest.quest_vr);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), quest.quest_shift);
+ SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), quest.quest_mask);
+ QuestOrComment rv = std::move(quest);
+ rv.span.begin = quest.questid.span.begin;
+ rv.span.end = quest.quest_mask.span.end;
+ return Some(Ok(std::move(rv)));
+ }
+} // namespace quest
+} // namespace ast
+} // namespace tmwa
diff --git a/src/ast/quest.hpp b/src/ast/quest.hpp
new file mode 100644
index 0000000..5112524
--- /dev/null
+++ b/src/ast/quest.hpp
@@ -0,0 +1,65 @@
+#pragma once
+// ast/quest.hpp - Structure of tmwa questdb
+//
+// Copyright © 2015 Ed Pasek <pasekei@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#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"
+
+namespace tmwa
+{
+namespace ast
+{
+namespace quest
+{
+ using io::Spanned;
+
+ struct Comment
+ {
+ RString comment;
+ };
+ struct Quest
+ {
+ Spanned<QuestId> questid;
+ Spanned<VarName> quest_var;
+ Spanned<VarName> quest_vr;
+ Spanned<int> quest_shift;
+ Spanned<int> quest_mask;
+ };
+
+ using QuestOrCommentBase = Variant<Comment, Quest>;
+ struct QuestOrComment : QuestOrCommentBase
+ {
+ QuestOrComment(Comment o) : QuestOrCommentBase(std::move(o)) {}
+ QuestOrComment(Quest o) : QuestOrCommentBase(std::move(o)) {}
+ io::LineSpan span;
+ };
+
+ Option<Result<QuestOrComment>> parse_quest(io::LineCharReader& lr);
+} // namespace quest
+} // namespace ast
+} // namespace tmwa
diff --git a/src/ast/script.cpp b/src/ast/script.cpp
new file mode 100644
index 0000000..ec958e1
--- /dev/null
+++ b/src/ast/script.cpp
@@ -0,0 +1,75 @@
+#include "script.hpp"
+// ast/script.cpp - Structure of tmwa-script
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "../io/line.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace ast
+{
+namespace script
+{
+ Result<ScriptBody> parse_script_body(io::LineCharReader& lr, ScriptOptions opt)
+ {
+ io::LineSpan span;
+ io::LineChar c;
+ while (true)
+ {
+ if (!lr.get(c))
+ {
+ return Err("error: unexpected EOF before '{' in parse_script_body"_s);
+ }
+ if (c.ch() == ' ' || (!opt.one_line && c.ch() == '\n'))
+ {
+ lr.adv();
+ continue;
+ }
+ break;
+ }
+ if (c.ch() != '{')
+ {
+ return Err(c.error_str("expected opening '{'"_s));
+ }
+
+ MString accum;
+ accum += c.ch();
+ span.begin = c;
+ lr.adv();
+ while (true)
+ {
+ if (!lr.get(c))
+ return Err(c.error_str("unexpected EOF before '}' in parse_script_body"_s));
+ if (opt.one_line && c.ch() == '\n')
+ return Err(c.error_str("unexpected EOL before '}' in parse_script_body"_s));
+ accum += c.ch();
+ span.end = c;
+ lr.adv();
+ if (c.ch() == '}')
+ {
+ return Ok(ScriptBody{RString(accum), std::move(span)});
+ }
+ }
+ }
+} // namespace script
+} // namespace ast
+} // namespace tmwa
diff --git a/src/ast/script.hpp b/src/ast/script.hpp
new file mode 100644
index 0000000..74b11e1
--- /dev/null
+++ b/src/ast/script.hpp
@@ -0,0 +1,112 @@
+#pragma once
+// ast/script.hpp - Structure of tmwa-script
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#include "../compat/result.hpp"
+
+#include "../io/span.hpp"
+
+
+namespace tmwa
+{
+namespace ast
+{
+namespace script
+{
+ using io::Spanned;
+
+ struct ScriptBody
+ {
+ RString braced_body;
+ io::LineSpan span;
+ };
+
+ struct ScriptOptions
+ {
+ // don't require a label at the beginning
+ bool implicit_start = false;
+ // label to generate at the beginning if not already present
+ RString default_label;
+ // beginning must be only 'end;'
+ bool no_start;
+ // don't requite an 'end;' at the end
+ 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);
+
+ /*
+ (-- First bare-body-chunk only allowed for npcs, items, magic, functions.
+ It is not allowed for events. Basically it's an implicit label at times.
+ Last normal-lines is only permitted on item and magic scripts. --)
+ { script-body }: "{" bare-body-chunk? body-chunk* normal-lines? "}"
+ body-chunk: (comment* labelname ":")+ bare-body-chunk
+ bare-body-chunk: normal-lines terminator-line
+ normal-lines: normal-line*
+ any-line: normal-line
+ any-line: terminator-line
+ normal-line: "if" "(" expr ")" any-line
+ normal-line: normal-command ((expr ",")* expr)? ";"
+ terminator-line: "menu" (expr, labelname)* expr, labelname ";"
+ terminator-line: "goto" labelname ";"
+ terminator-line: terminator ((expr ",")* expr)? ";"
+ terminator: "return"
+ terminator: "close"
+ terminator: "end"
+ terminator: "mapexit"
+ terminator: "shop"
+
+ expr: subexpr_-1
+ subexpr_N: ("+" | "-" | "!" | "~") subexpr_7
+ subexpr_N: simple-expr (op_M subexpr_M | "(" ((expr ",")+ expr)? ")")* if N < M; function call only if N < 8 and preceding simple-expr (op sub)* is a known function
+ op_0: "||"
+ op_1: "&&"
+ op_2: "=="
+ op_2: "!="
+ op_2: ">="
+ op_2: ">"
+ op_2: "<="
+ op_2: "<"
+ op_3: "^"
+ op_4: "|"
+ op_5: "&"
+ op_5: ">>"
+ op_5: "<<"
+ op_6: "+"
+ op_6: "-"
+ op_7: "*"
+ op_7: "/"
+ op_7: "%"
+ simple-expr: "(" expr ")"
+ simple-expr: integer
+ simple-expr: string
+ simple-expr: variable ("[" expr "]")?
+ simple-expr: function // no longer command/label though
+ */
+} // namespace script
+} // namespace ast
+} // namespace tmwa
diff --git a/src/char/char.cpp b/src/char/char.cpp
index d5e887b..ed9e369 100644
--- a/src/char/char.cpp
+++ b/src/char/char.cpp
@@ -49,13 +49,13 @@
#include "../generic/array.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
+#include "../io/extract.hpp"
#include "../io/lock.hpp"
#include "../io/read.hpp"
+#include "../io/span.hpp"
#include "../io/tty.hpp"
#include "../io/write.hpp"
-#include "../net/packets.hpp"
#include "../net/socket.hpp"
#include "../net/timer.hpp"
@@ -66,15 +66,23 @@
#include "../proto2/char-user.hpp"
#include "../mmo/config_parse.hpp"
-#include "../mmo/core.hpp"
-#include "../mmo/extract.hpp"
+#include "../mmo/cxxstdio_enums.hpp"
#include "../mmo/extract_enums.hpp"
#include "../mmo/human_time_diff.hpp"
-#include "../mmo/mmo.hpp"
-#include "../mmo/utils.hpp"
#include "../mmo/version.hpp"
+#include "../high/core.hpp"
+#include "../high/extract_mmo.hpp"
+#include "../high/mmo.hpp"
+#include "../high/utils.hpp"
+
+#include "../wire/packets.hpp"
+
+#include "char_conf.hpp"
+#include "char_lan_conf.hpp"
+#include "globals.hpp"
#include "inter.hpp"
+#include "inter_conf.hpp"
#include "int_party.hpp"
#include "int_storage.hpp"
@@ -83,58 +91,8 @@
namespace tmwa
{
-static
-Array<struct mmo_map_server, MAX_MAP_SERVERS> server;
-static
-Array<Session *, MAX_MAP_SERVERS> server_session;
-static
-Array<int, MAX_MAP_SERVERS> server_freezeflag; // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed
-static
-int anti_freeze_enable = 0;
-static
-std::chrono::seconds anti_freeze_interval = 6_s;
-
-constexpr
-std::chrono::milliseconds DEFAULT_AUTOSAVE_INTERVAL =
- 5_min;
-
-static
-Session *login_session, *char_session;
-static
-AccountName userid;
-static
-AccountPass passwd;
-static
-ServerName server_name;
-static
-CharName wisp_server_name = stringish<CharName>("Server"_s);
-static
-IP4Address login_ip;
-static
-int login_port = 6900;
-static
-IP4Address char_ip;
-static
-int char_port = 6121;
-static
-AString char_txt;
-static
-CharName unknown_char_name = stringish<CharName>("Unknown"_s);
-static
-AString char_log_filename = "log/char.log"_s;
-//Added for lan support
-static
-IP4Address lan_map_ip = IP4_LOCALHOST;
-static
-IP4Mask lan_subnet = IP4Mask(IP4_LOCALHOST, IP4_BROADCAST);
-static
-int char_name_option = 0; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor]
-static
-std::bitset<256> char_name_letters; // list of letters/symbols authorised (or not) in a character name. by [Yor]
-static constexpr
-GmLevel default_gm_level = GmLevel::from(0_u32);
-
-
+namespace char_
+{
struct char_session_data : SessionData
{
AccountId account_id;
@@ -142,69 +100,16 @@ struct char_session_data : SessionData
SEX sex;
unsigned short packet_tmw_version;
AccountEmail email;
- TimeT connect_until_time;
};
+} // namespace char_
void SessionDeleter::operator()(SessionData *sd)
{
- really_delete1 static_cast<char_session_data *>(sd);
+ really_delete1 static_cast<char_::char_session_data *>(sd);
}
-struct AuthFifoEntry
+namespace char_
{
- AccountId account_id;
- CharId char_id;
- int login_id1, login_id2;
- IP4Address ip;
- int delflag;
- SEX sex;
- unsigned short packet_tmw_version;
- TimeT connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
-};
-static
-std::array<AuthFifoEntry, 256> auth_fifo;
-static
-auto auth_fifo_iter = auth_fifo.begin();
-
-static
-int check_ip_flag = 1; // It's to check IP of a player between char-server and other servers (part of anti-hacking system)
-
-static
-CharId char_id_count = wrap<CharId>(150000);
-static
-std::vector<CharPair> char_keys;
-static
-int max_connect_user = 0;
-static
-std::chrono::milliseconds autosave_time = DEFAULT_AUTOSAVE_INTERVAL;
-
-// Initial position (it's possible to set it in conf file)
-static
-Point start_point = { {"001-1.gat"_s}, 273, 354 };
-
-static
-std::vector<GM_Account> gm_accounts;
-
-// online players by [Yor]
-static
-AString online_txt_filename = "online.txt"_s;
-static
-AString online_html_filename = "online.html"_s;
-static
-int online_sorting_option = 0; // sorting option to display online players in online files
-static
-int online_refresh_html = 20; // refresh time (in sec) of the html file in the explorer
-static
-GmLevel online_gm_display_min_level = GmLevel::from(20_u32); // minimum GM level to display 'GM' when we want to display it
-
-static
-std::vector<Session *> online_chars; // same size of char_keys, and id value of current server (or -1)
-static
-TimeT update_online; // to update online files when we receiving information from a server (not less than 8 seconds)
-
-static
-pid_t pid = 0; // For forked DB writes
-
auto iter_map_sessions() -> decltype(filter_iterator<Session *>(std::declval<Array<Session *, MAX_MAP_SERVERS> *>()))
{
@@ -250,7 +155,7 @@ void delete_frommap(Session *sess)
//------------------------------
void char_log(XString line)
{
- io::AppendFile logfp(char_log_filename, true);
+ io::AppendFile logfp(char_conf.char_log_filename, true);
if (!logfp.is_open())
return;
log_with_timestamp(logfp, line);
@@ -327,7 +232,7 @@ AString mmo_char_tostr(struct CharPair *cp)
// on multi-map server, sometimes it's posssible that last_point become void. (reason???) We check that to not lost character at restart.
if (!p->last_point.map_)
{
- p->last_point = start_point;
+ p->last_point = char_conf.start_point;
}
MString str_p;
@@ -414,12 +319,6 @@ AString mmo_char_tostr(struct CharPair *cp)
return AString(str_p);
}
-static
-bool extract(XString str, Point *p)
-{
- return extract(str, record<','>(&p->map_, &p->x, &p->y));
-}
-
struct skill_loader
{
SkillID id;
@@ -428,7 +327,7 @@ struct skill_loader
};
static
-bool extract(XString str, struct skill_loader *s)
+bool impl_extract(XString str, struct skill_loader *s)
{
uint32_t flags_and_level;
if (!extract(str,
@@ -438,13 +337,16 @@ bool extract(XString str, struct skill_loader *s)
s->flags = SkillFlags(flags_and_level >> 16);
return true;
}
+} // namespace char
//-------------------------------------------------------------------------
// Function to set the character from the line (at read of characters file)
//-------------------------------------------------------------------------
static
-bool extract(XString str, CharPair *cp)
+bool impl_extract(XString str, CharPair *cp)
{
+ using namespace tmwa::char_;
+
CharKey *k = &cp->key;
CharData *p = cp->data.get();
@@ -489,7 +391,7 @@ bool extract(XString str, CharPair *cp)
else if (!extract(hair_style, &p->hair))
return false;
- if (wisp_server_name == k->name)
+ if (WISP_SERVER_NAME == k->name)
return false;
// TODO replace *every* lookup with a map lookup
@@ -532,6 +434,8 @@ bool extract(XString str, CharPair *cp)
return true;
}
+namespace char_
+{
//---------------------------------
// Function to read characters file
//---------------------------------
@@ -541,11 +445,11 @@ int mmo_char_init(void)
char_keys.clear();
online_chars.clear();
- io::ReadFile in(char_txt);
+ io::ReadFile in(char_conf.char_txt);
if (!in.is_open())
{
- PRINTF("Characters file not found: %s.\n"_fmt, char_txt);
- CHAR_LOG("Characters file not found: %s.\n"_fmt, char_txt);
+ PRINTF("Characters file not found: %s.\n"_fmt, char_conf.char_txt);
+ CHAR_LOG("Characters file not found: %s.\n"_fmt, char_conf.char_txt);
CHAR_LOG("Id for the next created character: %d.\n"_fmt,
char_id_count);
return 0;
@@ -583,9 +487,9 @@ int mmo_char_init(void)
}
PRINTF("mmo_char_init: %zu characters read in %s.\n"_fmt,
- char_keys.size(), char_txt);
+ char_keys.size(), char_conf.char_txt);
CHAR_LOG("mmo_char_init: %zu characters read in %s.\n"_fmt,
- char_keys.size(), char_txt);
+ char_keys.size(), char_conf.char_txt);
CHAR_LOG("Id for the next created character: %d.\n"_fmt,
char_id_count);
@@ -599,7 +503,7 @@ int mmo_char_init(void)
static
void mmo_char_sync(void)
{
- io::WriteLock fp(char_txt);
+ io::WriteLock fp(char_conf.char_txt);
if (!fp.is_open())
{
PRINTF("WARNING: Server can't not save characters.\n"_fmt);
@@ -687,28 +591,18 @@ CharPair *make_new_char(Session *s, CharName name, const Stats6& stats, uint8_t
}
// Check Authorised letters/symbols in the name of the character
- if (char_name_option == 1)
{
// only letters/symbols in char_name_letters are authorised
for (uint8_t c : name.to__actual())
- if (!char_name_letters[c])
+ {
+ if (!char_conf.char_name_letters[c])
{
CHAR_LOG("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n"_fmt,
s, sd->account_id, name, c);
return nullptr;
}
+ }
}
- else if (char_name_option == 2)
- {
- // letters/symbols in char_name_letters are forbidden
- for (uint8_t c : name.to__actual())
- if (char_name_letters[c])
- {
- CHAR_LOG("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n"_fmt,
- s, sd->account_id, name, c);
- return nullptr;
- }
- } // else, all letters/symbols are authorised (except control char removed before)
// TODO this comment is obsolete
// this is why it needs to be unsigned
@@ -763,10 +657,10 @@ CharPair *make_new_char(Session *s, CharName name, const Stats6& stats, uint8_t
}
}
- if (wisp_server_name == name)
+ if (WISP_SERVER_NAME == name)
{
CHAR_LOG("Make new char error (name used is wisp name for server): (connection #%d, account: %d) slot %d, name: %s (actual name whisper server: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n"_fmt,
- s, sd->account_id, slot, name, wisp_server_name,
+ s, sd->account_id, slot, name, WISP_SERVER_NAME,
stats.str, stats.agi, stats.vit, stats.int_, stats.dex, stats.luk,
stats.str + stats.agi + stats.vit + stats.int_ + stats.dex + stats.luk,
hair_style, hair_color);
@@ -807,7 +701,7 @@ CharPair *make_new_char(Session *s, CharName name, const Stats6& stats, uint8_t
cd.sp = cd.max_sp;
cd.status_point = 0;
cd.skill_point = 0;
- cd.option = static_cast<Option>(0x0000); // Option is only declared
+ cd.option = static_cast<Opt0>(0x0000); // Opt0 is only declared
cd.karma = 0;
cd.manner = 0;
cd.party_id = PartyId();
@@ -821,8 +715,8 @@ CharPair *make_new_char(Session *s, CharName name, const Stats6& stats, uint8_t
cd.head_top = ItemNameId();
cd.head_mid = ItemNameId();
cd.head_bottom = ItemNameId();
- cd.last_point = start_point;
- cd.save_point = start_point;
+ cd.last_point = char_conf.start_point;
+ cd.save_point = char_conf.start_point;
char_keys.push_back(std::move(cp));
online_chars.push_back(nullptr);
@@ -836,10 +730,10 @@ static
void create_online_files(void)
{
// write files
- io::WriteFile fp(online_txt_filename);
+ io::WriteFile fp(char_conf.online_txt_filename);
if (fp.is_open())
{
- io::WriteFile fp2(online_html_filename);
+ io::WriteFile fp2(char_conf.online_html_filename);
if (fp2.is_open())
{
// get time
@@ -847,15 +741,15 @@ void create_online_files(void)
stamp_time(timetemp);
// write heading
FPRINTF(fp2, "<HTML>\n"_fmt);
- FPRINTF(fp2, " <META http-equiv=\"Refresh\" content=\"%d\">\n"_fmt, online_refresh_html); // update on client explorer every x seconds
+ FPRINTF(fp2, " <META http-equiv=\"Refresh\" content=\"%d\">\n"_fmt, char_conf.online_refresh_html); // update on client explorer every x seconds
FPRINTF(fp2, " <HEAD>\n"_fmt);
FPRINTF(fp2, " <TITLE>Online Players on %s</TITLE>\n"_fmt,
- server_name);
+ char_conf.server_name);
FPRINTF(fp2, " </HEAD>\n"_fmt);
FPRINTF(fp2, " <BODY>\n"_fmt);
FPRINTF(fp2, " <H3>Online Players on %s (%s):</H3>\n"_fmt,
- server_name, timetemp);
- FPRINTF(fp, "Online Players on %s (%s):\n"_fmt, server_name, timetemp);
+ char_conf.server_name, timetemp);
+ FPRINTF(fp, "Online Players on %s (%s):\n"_fmt, char_conf.server_name, timetemp);
FPRINTF(fp, "\n"_fmt);
int players = 0;
@@ -891,14 +785,14 @@ void create_online_files(void)
// without/with 'GM' display
GmLevel gml = isGM(cd.key.account_id);
{
- if (gml.satisfies(online_gm_display_min_level))
+ if (gml.satisfies(char_conf.online_gm_display_min_level))
FPRINTF(fp, "%-24s (GM) "_fmt, cd.key.name);
else
FPRINTF(fp, "%-24s "_fmt, cd.key.name);
}
// name of the character in the html (no < >, because that create problem in html code)
FPRINTF(fp2, " <td>"_fmt);
- if (gml.satisfies(online_gm_display_min_level))
+ if (gml.satisfies(char_conf.online_gm_display_min_level))
FPRINTF(fp2, "<b>"_fmt);
for (char c : cd.key.name.to__actual())
{
@@ -918,7 +812,7 @@ void create_online_files(void)
break;
};
}
- if (gml.satisfies(online_gm_display_min_level))
+ if (gml.satisfies(char_conf.online_gm_display_min_level))
FPRINTF(fp2, "</b> (GM)"_fmt);
FPRINTF(fp2, "</td>\n"_fmt);
}
@@ -1278,13 +1172,12 @@ void parse_tologin(Session *ls)
fixed_6c.code = 0x42;
send_fpacket<0x006c, 3>(s2, fixed_6c);
}
- else if (max_connect_user == 0
- || count_users() < max_connect_user)
+ else if (char_conf.max_connect_user == 0
+ || count_users() < char_conf.max_connect_user)
{
sd->email = stringish<AccountEmail>(fixed.email);
if (!e_mail_check(sd->email))
sd->email = DEFAULT_EMAIL;
- sd->connect_until_time = fixed.connect_until;
// send characters to player
mmo_char_send006b(s2, sd);
}
@@ -1323,7 +1216,6 @@ void parse_tologin(Session *ls)
sd->email = fixed.email;
if (!e_mail_check(sd->email))
sd->email = DEFAULT_EMAIL;
- sd->connect_until_time = fixed.connect_until;
break;
}
}
@@ -1331,28 +1223,6 @@ void parse_tologin(Session *ls)
break;
}
- case 0x2721: // gm reply
- {
- Packet_Fixed<0x2721> fixed;
- rv = recv_fpacket<0x2721, 10>(ls, fixed);
- if (rv != RecvResult::Complete)
- break;
-
- {
- AccountId acc = fixed.account_id;
- GmLevel gml = fixed.gm_level;
-
- Packet_Fixed<0x2b0b> fixed_2b;
- fixed_2b.account_id = acc;
- fixed_2b.gm_level = gml;
- for (Session *ss : iter_map_sessions())
- {
- send_fpacket<0x2b0b, 10>(ss, fixed_2b);
- }
- }
- break;
- }
-
case 0x2723: // changesex reply (modified by [Yor])
{
Packet_Fixed<0x2723> fixed;
@@ -1481,65 +1351,6 @@ void parse_tologin(Session *ls)
break;
}
- case 0x7924:
- { // [Fate] Itemfrob package: forwarded from login-server
- Packet_Fixed<0x7924> fixed;
- rv = recv_fpacket<0x7924, 10>(ls, fixed);
- if (rv != RecvResult::Complete)
- break;
-
- ItemNameId source_id = fixed.source_item_id;
- ItemNameId dest_id = fixed.dest_item_id;
-
- Packet_Fixed<0x2afa> fixed_fa;
- fixed_fa.source_item_id = source_id;
- fixed_fa.dest_item_id = dest_id;
-
- // forward package to map servers
- for (Session *ss : iter_map_sessions())
- {
- send_fpacket<0x2afa, 10>(ss, fixed_fa);
- }
-
- for (CharPair& cp : char_keys)
- {
- CharKey *k = &cp.key;
- CharData& cd = *cp.data.get();
- CharData *c = &cd;
- Storage *s = account2storage(k->account_id);
- int changes = 0;
-#define FIX(v) if (v == source_id) {v = dest_id; ++changes; }
- for (IOff0 j : IOff0::iter())
- {
- FIX(c->inventory[j].nameid);
- }
- // used to FIX cart, but it's no longer supported
- // FIX(c->weapon);
- FIX(c->shield);
- FIX(c->head_top);
- FIX(c->head_mid);
- FIX(c->head_bottom);
-
- if (s)
- {
- for (SOff0 j : SOff0::iter())
- {
- FIX(s->storage_[j].nameid);
- }
- }
-#undef FIX
- if (changes)
- CHAR_LOG("itemfrob(%d -> %d): `%s'(%d, account %d): changed %d times\n"_fmt,
- source_id, dest_id, k->name, k->char_id,
- k->account_id, changes);
-
- }
-
- mmo_char_sync();
- inter_storage_save();
- break;
- }
-
// Account deletion notification (from login-server)
case 0x2730:
{
@@ -1737,22 +1548,6 @@ void parse_frommap(Session *ms)
{
switch (packet_id)
{
- // request from map-server to reload GM accounts. Transmission to login-server (by Yor)
- case 0x2af7:
- {
- Packet_Fixed<0x2af7> fixed;
- rv = recv_fpacket<0x2af7, 2>(ms, fixed);
- if (rv != RecvResult::Complete)
- break;
-
- if (login_session)
- { // don't send request if no login-server
- Packet_Fixed<0x2709> fixed_09;
- send_fpacket<0x2709, 2>(login_session, fixed_09);
- }
- break;
- }
-
// Receiving map names list from the map-server
case 0x2afa:
{
@@ -1781,7 +1576,6 @@ void parse_frommap(Session *ms)
Packet_Fixed<0x2afb> fixed_fb;
fixed_fb.unknown = 0;
- fixed_fb.whisper_name = wisp_server_name;
send_fpacket<0x2afb, 27>(ms, fixed_fb);
{
@@ -1857,7 +1651,7 @@ void parse_frommap(Session *ms)
afi.login_id1 == login_id1 &&
// here, it's the only area where it's possible that we doesn't know login_id2 (map-server asks just after 0x72 packet, that doesn't given the value)
(afi.login_id2 == login_id2 || login_id2 == 0) && // relate to the versions higher than 18
- (!check_ip_flag || afi.ip == ip)
+ afi.ip == ip
&& !afi.delflag)
{
CharPair *cp = nullptr;
@@ -1878,7 +1672,6 @@ void parse_frommap(Session *ms)
Packet_Payload<0x2afd> payload_fd; // not file descriptor
payload_fd.account_id = account_id;
payload_fd.login_id2 = afi.login_id2;
- payload_fd.connect_until = afi.connect_until_time;
cd->sex = afi.sex;
payload_fd.packet_tmw_version = afi.packet_tmw_version;
FPRINTF(stderr,
@@ -1912,7 +1705,7 @@ void parse_frommap(Session *ms)
server[id].users = head.users;
assert (head.users == repeat.size());
- if (anti_freeze_enable)
+ if (char_conf.anti_freeze_enable)
server_freezeflag[id] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed
// remove all previously online players of the server
for (Session *& oci : online_chars)
@@ -1984,7 +1777,6 @@ void parse_frommap(Session *ms)
auth_fifo_iter->login_id1 = fixed.login_id1;
auth_fifo_iter->login_id2 = fixed.login_id2;
auth_fifo_iter->delflag = 2;
- auth_fifo_iter->connect_until_time = TimeT(); // unlimited/unknown time by default (not display in map-server)
auth_fifo_iter->ip = fixed.ip;
auth_fifo_iter++;
@@ -2023,7 +1815,6 @@ void parse_frommap(Session *ms)
auth_fifo_iter->char_id = fixed.char_id;
auth_fifo_iter->delflag = 0;
auth_fifo_iter->sex = fixed.sex;
- auth_fifo_iter->connect_until_time = TimeT(); // unlimited/unknown time by default (not display in map-server)
auth_fifo_iter->ip = fixed.client_ip;
// default, if not found in the loop
@@ -2044,33 +1835,6 @@ void parse_frommap(Session *ms)
break;
}
- // it is a request to become GM
- case 0x2b0a:
- {
- Packet_Head<0x2b0a> head;
- AString repeat;
- rv = recv_vpacket<0x2b0a, 8, 1>(ms, head, repeat);
- if (rv != RecvResult::Complete)
- break;
-
- AccountId account_id = head.account_id;
- if (login_session)
- { // don't send request if no login-server
- Packet_Head<0x2720> head_20;
- head_20.account_id = account_id;
- AString& repeat_20 = repeat;
- send_vpacket<0x2720, 8, 1>(login_session, head_20, repeat_20);
- }
- else
- {
- Packet_Fixed<0x2b0b> fixed_0b;
- fixed_0b.account_id = account_id;
- fixed_0b.gm_level = GmLevel();
- send_fpacket<0x2b0b, 10>(ms, fixed_0b);
- }
- break;
- }
-
// Map server send information to change an email of an account -> login-server
case 0x2b0c:
{
@@ -2336,7 +2100,7 @@ int search_mapserver(XString map)
static
int lan_ip_check(IP4Address addr)
{
- bool lancheck = lan_subnet.covers(addr);
+ bool lancheck = char_lan_conf.lan_subnet.covers(addr);
PRINTF("LAN test (result): %s.\n"_fmt,
(lancheck) ? SGR_BOLD SGR_CYAN "LAN source" SGR_RESET ""_s : SGR_BOLD SGR_GREEN "WAN source" SGR_RESET ""_s);
@@ -2401,7 +2165,7 @@ void handle_x0066(Session *s, struct char_session_data *sd, uint8_t rfifob_2, IP
sd->account_id, ck->char_num, ip);
PRINTF("--Send IP of map-server. "_fmt);
if (lan_ip_check(ip))
- fixed_71.ip = lan_map_ip;
+ fixed_71.ip = char_lan_conf.lan_map_ip;
else
fixed_71.ip = server[i].ip;
fixed_71.port = server[i].port;
@@ -2415,7 +2179,6 @@ void handle_x0066(Session *s, struct char_session_data *sd, uint8_t rfifob_2, IP
auth_fifo_iter->login_id2 = sd->login_id2;
auth_fifo_iter->delflag = 0;
auth_fifo_iter->sex = sd->sex;
- auth_fifo_iter->connect_until_time = sd->connect_until_time;
auth_fifo_iter->ip = s->client_ip;
auth_fifo_iter->packet_tmw_version = sd->packet_tmw_version;
auth_fifo_iter++;
@@ -2484,7 +2247,6 @@ void parse_char(Session *s)
s->session_data = make_unique<char_session_data, SessionDeleter>();
sd = static_cast<char_session_data *>(s->session_data.get());
sd->email = stringish<AccountEmail>("no mail"_s); // put here a mail without '@' to refuse deletion if we don't receive the e-mail
- sd->connect_until_time = TimeT(); // unknow or illimited (not displaying on map-server)
}
sd->account_id = account_id;
sd->login_id1 = fixed.login_id1;
@@ -2503,13 +2265,12 @@ void parse_char(Session *s)
if (afi.account_id == sd->account_id
&& afi.login_id1 == sd->login_id1
&& afi.login_id2 == sd->login_id2
- && (!check_ip_flag
- || afi.ip == s->client_ip)
+ && afi.ip == s->client_ip
&& afi.delflag == 2)
{
afi.delflag = 1;
- if (max_connect_user == 0
- || count_users() < max_connect_user)
+ if (char_conf.max_connect_user == 0
+ || count_users() < char_conf.max_connect_user)
{
{
// there is always a login server
@@ -2608,7 +2369,7 @@ void parse_char(Session *s)
fixed_6d.char_select.gloves = ItemNameId();
fixed_6d.char_select.cape = ItemNameId();
fixed_6d.char_select.misc1 = ItemNameId();
- fixed_6d.char_select.option = Option();
+ fixed_6d.char_select.option = Opt0();
fixed_6d.char_select.unused = 0;
// this was buggy until the protocol became generated
@@ -2663,9 +2424,6 @@ void parse_char(Session *s)
s->set_eof();
return;
}
- AccountEmail email = fixed.email;
- if (!e_mail_check(email))
- email = DEFAULT_EMAIL;
{
{
@@ -2723,8 +2481,8 @@ void parse_char(Session *s)
}
AccountName userid_ = fixed.account_name;
AccountPass passwd_ = fixed.account_pass;
- if (i == MAX_MAP_SERVERS || userid_ != userid
- || passwd_ != passwd)
+ if (i == MAX_MAP_SERVERS || userid_ != char_conf.userid
+ || passwd_ != char_conf.passwd)
{
fixed_f9.code = 3;
send_fpacket<0x2af9, 3>(s, fixed_f9);
@@ -2734,7 +2492,7 @@ void parse_char(Session *s)
fixed_f9.code = 0;
s->set_parsers(SessionParsers{.func_parse= parse_frommap, .func_delete= delete_frommap});
server_session[i] = s;
- if (anti_freeze_enable)
+ if (char_conf.anti_freeze_enable)
server_freezeflag[i] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed
// ignore fixed.unknown
server[i].ip = fixed.ip;
@@ -2822,19 +2580,19 @@ void check_connect_login_server(TimerData *, tick_t)
if (!login_session)
{
PRINTF("Attempt to connect to login-server...\n"_fmt);
- login_session = make_connection(login_ip, login_port,
+ login_session = make_connection(char_conf.login_ip, char_conf.login_port,
SessionParsers{.func_parse= parse_tologin, .func_delete= delete_tologin});
if (!login_session)
return;
realloc_fifo(login_session, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
Packet_Fixed<0x2710> fixed_10;
- fixed_10.account_name = userid;
- fixed_10.account_pass = passwd;
+ fixed_10.account_name = char_conf.userid;
+ fixed_10.account_pass = char_conf.passwd;
fixed_10.unknown = 0;
- fixed_10.ip = char_ip;
- fixed_10.port = char_port;
- fixed_10.server_name = server_name;
+ fixed_10.ip = char_conf.char_ip;
+ fixed_10.port = char_conf.char_port;
+ fixed_10.server_name = char_conf.server_name;
fixed_10.unknown2 = 0;
fixed_10.maintenance = 0;
fixed_10.is_new = 0;
@@ -2842,61 +2600,13 @@ void check_connect_login_server(TimerData *, tick_t)
}
}
-//-------------------------------------------
-// Reading Lan Support configuration by [Yor]
-//-------------------------------------------
-static
-bool char_lan_config(XString w1, ZString w2)
-{
- struct hostent *h = nullptr;
-
- {
- if (w1 == "lan_map_ip"_s)
- {
- // Read map-server Lan IP Address
- h = gethostbyname(w2.c_str());
- if (h != nullptr)
- {
- lan_map_ip = IP4Address({
- static_cast<uint8_t>(h->h_addr[0]),
- static_cast<uint8_t>(h->h_addr[1]),
- static_cast<uint8_t>(h->h_addr[2]),
- static_cast<uint8_t>(h->h_addr[3]),
- });
- }
- else
- {
- PRINTF("Bad IP value: %s\n"_fmt, w2);
- return false;
- }
- PRINTF("LAN IP of map-server: %s.\n"_fmt, lan_map_ip);
- }
- else if (w1 == "subnet"_s /*backward compatibility*/
- || w1 == "lan_subnet"_s)
- {
- if (!extract(w2, &lan_subnet))
- {
- PRINTF("Bad IP mask: %s\n"_fmt, w2);
- return false;
- }
- PRINTF("Sub-network of the map-server: %s.\n"_fmt,
- lan_subnet);
- }
- else
- {
- return false;
- }
- }
- return true;
-}
-
static
bool lan_check()
{
// sub-network check of the map-server
{
PRINTF("LAN test of LAN IP of the map-server: "_fmt);
- if (!lan_ip_check(lan_map_ip))
+ if (!lan_ip_check(char_lan_conf.lan_map_ip))
{
PRINTF(SGR_BOLD SGR_RED "***ERROR: LAN IP of the map-server doesn't belong to the specified Sub-network." SGR_RESET "\n"_fmt);
return false;
@@ -2907,161 +2617,46 @@ bool lan_check()
}
static
-bool char_config(XString w1, ZString w2)
+bool char_config(io::Spanned<XString> key, io::Spanned<ZString> value)
{
- struct hostent *h = nullptr;
+ return parse_char_conf(char_conf, key, value);
+}
+static
+bool char_lan_config(io::Spanned<XString> key, io::Spanned<ZString> value)
+{
+ return parse_char_lan_conf(char_lan_conf, key, value);
+}
+
+static
+bool inter_config(io::Spanned<XString> key, io::Spanned<ZString> value)
+{
+ return parse_inter_conf(inter_conf, key, value);
+}
+
+static
+bool char_confs(io::Spanned<XString> key, io::Spanned<ZString> value)
+{
+ if (key.data == "char_conf"_s)
{
- if (w1 == "userid"_s)
- userid = stringish<AccountName>(w2);
- else if (w1 == "passwd"_s)
- passwd = stringish<AccountPass>(w2);
- else if (w1 == "server_name"_s)
- {
- server_name = stringish<ServerName>(w2);
- PRINTF("%s server has been intialized\n"_fmt, w2);
- }
- else if (w1 == "wisp_server_name"_s)
- {
- if (w2.size() >= 4)
- wisp_server_name = stringish<CharName>(w2);
- }
- else if (w1 == "login_ip"_s)
- {
- h = gethostbyname(w2.c_str());
- if (h != nullptr)
- {
- login_ip = IP4Address({
- static_cast<uint8_t>(h->h_addr[0]),
- static_cast<uint8_t>(h->h_addr[1]),
- static_cast<uint8_t>(h->h_addr[2]),
- static_cast<uint8_t>(h->h_addr[3]),
- });
- PRINTF("Login server IP address : %s -> %s\n"_fmt,
- w2, login_ip);
- }
- else
- {
- PRINTF("Bad IP value: %s\n"_fmt, w2);
- return false;
- }
- }
- else if (w1 == "login_port"_s)
- {
- login_port = atoi(w2.c_str());
- }
- else if (w1 == "char_ip"_s)
- {
- h = gethostbyname(w2.c_str());
- if (h != nullptr)
- {
- char_ip = IP4Address({
- static_cast<uint8_t>(h->h_addr[0]),
- static_cast<uint8_t>(h->h_addr[1]),
- static_cast<uint8_t>(h->h_addr[2]),
- static_cast<uint8_t>(h->h_addr[3]),
- });
- PRINTF("Character server IP address : %s -> %s\n"_fmt,
- w2, char_ip);
- }
- else
- {
- PRINTF("Bad IP value: %s\n"_fmt, w2);
- return false;
- }
- }
- else if (w1 == "char_port"_s)
- {
- char_port = atoi(w2.c_str());
- }
- else if (w1 == "char_txt"_s)
- {
- char_txt = w2;
- }
- else if (w1 == "max_connect_user"_s)
- {
- max_connect_user = atoi(w2.c_str());
- if (max_connect_user < 0)
- max_connect_user = 0; // unlimited online players
- }
- else if (w1 == "check_ip_flag"_s)
- {
- check_ip_flag = config_switch(w2);
- }
- else if (w1 == "autosave_time"_s)
- {
- autosave_time = std::chrono::seconds(atoi(w2.c_str()));
- if (autosave_time <= std::chrono::seconds::zero())
- autosave_time = DEFAULT_AUTOSAVE_INTERVAL;
- }
- else if (w1 == "start_point"_s)
- {
- extract(w2, &start_point);
- }
- else if (w1 == "unknown_char_name"_s)
- {
- unknown_char_name = stringish<CharName>(w2);
- }
- else if (w1 == "char_log_filename"_s)
- {
- char_log_filename = w2;
- }
- else if (w1 == "char_name_option"_s)
- {
- char_name_option = atoi(w2.c_str());
- }
- else if (w1 == "char_name_letters"_s)
- {
- if (!w2)
- char_name_letters.reset();
- else
- for (uint8_t c : w2)
- char_name_letters[c] = true;
- }
- else if (w1 == "online_txt_filename"_s)
- {
- online_txt_filename = w2;
- }
- else if (w1 == "online_html_filename"_s)
- {
- online_html_filename = w2;
- }
- else if (w1 == "online_sorting_option"_s)
- {
- online_sorting_option = atoi(w2.c_str());
- }
- else if (w1 == "online_gm_display_min_level"_s)
- {
- // minimum GM level to display 'GM' when we want to display it
- return extract(w2, &online_gm_display_min_level);
- }
- else if (w1 == "online_refresh_html"_s)
- {
- online_refresh_html = atoi(w2.c_str());
- if (online_refresh_html < 1)
- online_refresh_html = 1;
- }
- else if (w1 == "anti_freeze_enable"_s)
- {
- anti_freeze_enable = config_switch(w2);
- }
- else if (w1 == "anti_freeze_interval"_s)
- {
- anti_freeze_interval = std::max(
- std::chrono::seconds(atoi(w2.c_str())),
- 5_s);
- }
- else
- {
- return false;
- }
+ return load_config_file(value.data, char_config);
}
-
- return true;
+ if (key.data == "char_lan_conf"_s)
+ {
+ return load_config_file(value.data, char_lan_config);
+ }
+ if (key.data == "inter_conf"_s)
+ {
+ return load_config_file(value.data, inter_config);
+ }
+ key.span.error("Unknown meta-key for char server"_s);
+ return false;
}
+} // namespace char_
void term_func(void)
{
+ using namespace tmwa::char_;
// write online players files with no player
std::fill(online_chars.begin(), online_chars.end(), nullptr);
create_online_files();
@@ -3079,20 +2674,9 @@ void term_func(void)
CHAR_LOG("----End of char-server (normal end with closing of all files).\n"_fmt);
}
-static
-bool char_confs(XString key, ZString value)
-{
- unsigned sum = 0;
- sum += char_config(key, value);
- sum += char_lan_config(key, value);
- sum += inter_config(key, value);
- if (sum >= 2)
- abort();
- return sum;
-}
-
int do_init(Slice<ZString> argv)
{
+ using namespace tmwa::char_;
ZString argv0 = argv.pop_front();
bool loaded_config_yet = false;
@@ -3121,12 +2705,12 @@ int do_init(Slice<ZString> argv)
else
{
loaded_config_yet = true;
- runflag &= load_config_file(argvi, char_confs);
+ runflag &= load_config_file(argvi, char_::char_confs);
}
}
if (!loaded_config_yet)
- runflag &= load_config_file("conf/tmwa-char.conf"_s, char_confs);
+ runflag &= load_config_file("conf/tmwa-char.conf"_s, char_::char_confs);
// a newline in the log...
CHAR_LOG(""_fmt);
@@ -3140,7 +2724,7 @@ int do_init(Slice<ZString> argv)
update_online = TimeT::now();
create_online_files(); // update online players files at start of the server
- char_session = make_listen_port(char_port, SessionParsers{parse_char, delete_char});
+ char_session = make_listen_port(char_conf.char_port, SessionParsers{parse_char, delete_char});
Timer(gettick() + 1_s,
check_connect_login_server,
@@ -3150,25 +2734,26 @@ int do_init(Slice<ZString> argv)
send_users_tologin,
5_s
).detach();
- Timer(gettick() + autosave_time,
+ Timer(gettick() + char_conf.autosave_time,
mmo_char_sync_timer,
- autosave_time
+ char_conf.autosave_time
).detach();
- if (anti_freeze_enable > 0)
+ if (char_conf.anti_freeze_enable > 0)
{
Timer(gettick() + 1_s,
map_anti_freeze_system,
- anti_freeze_interval
+ char_conf.anti_freeze_interval
).detach();
}
CHAR_LOG("The char-server is ready (Server is listening on the port %d).\n"_fmt,
- char_port);
+ char_conf.char_port);
PRINTF("The char-server is " SGR_BOLD SGR_GREEN "ready" SGR_RESET " (Server is listening on the port %d).\n\n"_fmt,
- char_port);
+ char_conf.char_port);
return 0;
}
+// namespace char_ ends before term_func and do_init
} // namespace tmwa
diff --git a/src/char/char.hpp b/src/char/char.hpp
index a9c786f..4f55c04 100644
--- a/src/char/char.hpp
+++ b/src/char/char.hpp
@@ -22,18 +22,36 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
+#include "../ints/udl.hpp"
#include "../generic/array.hpp"
#include "../net/ip.hpp"
-#include "../mmo/mmo.hpp"
+#include "../high/mmo.hpp"
+
+#include "consts.hpp"
namespace tmwa
{
-constexpr int MAX_MAP_SERVERS = 30;
+namespace char_
+{
+constexpr
+std::chrono::seconds DEFAULT_AUTOSAVE_INTERVAL = 5_min;
+constexpr
+GmLevel default_gm_level = GmLevel::from(0_u32);
+
+struct AuthFifoEntry
+{
+ AccountId account_id;
+ CharId char_id;
+ int login_id1, login_id2;
+ IP4Address ip;
+ int delflag;
+ SEX sex;
+ unsigned short packet_tmw_version;
+};
struct mmo_map_server
{
@@ -53,4 +71,5 @@ void char_log(XString line);
#define CHAR_LOG(fmt, ...) \
char_log(STRPRINTF(fmt, ## __VA_ARGS__))
+} // namespace char_
} // namespace tmwa
diff --git a/src/map/magic-interpreter.cpp b/src/char/consts.hpp
index 389a821..e3b6c57 100644
--- a/src/map/magic-interpreter.cpp
+++ b/src/char/consts.hpp
@@ -1,5 +1,5 @@
-#include "magic-interpreter.hpp"
-// magic-interpreter.cpp - Old magic.
+#pragma once
+// consts.hpp - Constants for tmwa-char.
//
// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
//
@@ -18,12 +18,14 @@
// 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 "../poison.hpp"
+#include "fwd.hpp"
namespace tmwa
{
-namespace magic
+namespace char_
{
-} // namespace magic
+constexpr
+int MAX_MAP_SERVERS = 30;
+} // namespace char_
} // namespace tmwa
diff --git a/src/char/fwd.hpp b/src/char/fwd.hpp
index 31cd1ba..8086083 100644
--- a/src/char/fwd.hpp
+++ b/src/char/fwd.hpp
@@ -20,8 +20,31 @@
#include "../sanity.hpp"
+#include "../ints/fwd.hpp" // rank 1
+#include "../strings/fwd.hpp" // rank 1
+#include "../compat/fwd.hpp" // rank 2
+#include "../generic/fwd.hpp" // rank 3
+#include "../io/fwd.hpp" // rank 4
+#include "../net/fwd.hpp" // rank 5
+#include "../mmo/fwd.hpp" // rank 6
+#include "../proto2/fwd.hpp" // rank 8
+#include "../high/fwd.hpp" // rank 9
+#include "../wire/fwd.hpp" // rank 9
+// char/fwd.hpp is rank ∞ because it is an executable
+
namespace tmwa
{
-// meh, add more when I feel like it
+namespace char_
+{
+ struct CharConf;
+ struct CharLanConf;
+ struct InterConf;
+
+ struct AuthFifoEntry;
+ struct mmo_map_server;
+
+ struct accreg;
+ // meh, add more when I feel like it
+} // namespace char_
} // namespace tmwa
diff --git a/src/char/globals.cpp b/src/char/globals.cpp
new file mode 100644
index 0000000..6733ad5
--- /dev/null
+++ b/src/char/globals.cpp
@@ -0,0 +1,68 @@
+#include "globals.hpp"
+// globals.cpp - Evil global variables for tmwa-char.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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/time_t.hpp"
+
+#include "../generic/db.hpp"
+
+#include "../proto2/net-Storage.hpp"
+
+#include "char.hpp"
+#include "char_conf.hpp"
+#include "char_lan_conf.hpp"
+#include "inter.hpp"
+#include "inter_conf.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+ namespace char_
+ {
+ CharConf char_conf;
+ CharLanConf char_lan_conf;
+ InterConf inter_conf;
+ Array<mmo_map_server, MAX_MAP_SERVERS> server;
+ Array<Session *, MAX_MAP_SERVERS> server_session;
+ // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed
+ Array<int, MAX_MAP_SERVERS> server_freezeflag;
+ Session *login_session, *char_session;
+ const CharName WISP_SERVER_NAME = stringish<CharName>("Server"_s);
+ std::array<AuthFifoEntry, 256> auth_fifo;
+ decltype(auth_fifo)::iterator auth_fifo_iter = auth_fifo.begin();
+ CharId char_id_count = wrap<CharId>(150000);
+ std::vector<CharPair> char_keys;
+ std::vector<GM_Account> gm_accounts;
+ // same size of char_keys, and id value of current server (or -1)
+ std::vector<Session *> online_chars;
+ // to update online files when we receiving information from a server (not less than 8 seconds)
+ TimeT update_online;
+ // For forked DB writes
+ pid_t pid = 0;
+
+ Map<AccountId, accreg> accreg_db;
+
+ Map<PartyId, PartyMost> party_db;
+ PartyId party_newid = wrap<PartyId>(100_u32);
+
+ Map<AccountId, Storage> storage_db;
+ } // namespace char_
+} // namespace tmwa
diff --git a/src/char/globals.hpp b/src/char/globals.hpp
new file mode 100644
index 0000000..2df3f21
--- /dev/null
+++ b/src/char/globals.hpp
@@ -0,0 +1,59 @@
+#pragma once
+// globals.hpp - Evil global variables for tmwa-char.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+#include <sys/types.h>
+
+#include <array>
+#include <vector>
+
+#include "consts.hpp"
+
+
+namespace tmwa
+{
+ namespace char_
+ {
+ extern CharConf char_conf;
+ extern CharLanConf char_lan_conf;
+ extern InterConf inter_conf;
+ extern Array<mmo_map_server, MAX_MAP_SERVERS> server;
+ extern Array<Session *, MAX_MAP_SERVERS> server_session;
+ extern Array<int, MAX_MAP_SERVERS> server_freezeflag;
+ extern Session *login_session, *char_session;
+ extern const CharName WISP_SERVER_NAME;
+ extern std::array<AuthFifoEntry, 256> auth_fifo;
+ extern AuthFifoEntry *auth_fifo_iter;
+ extern CharId char_id_count;
+ extern std::vector<CharPair> char_keys;
+ extern std::vector<GM_Account> gm_accounts;
+ extern std::vector<Session *> online_chars;
+ extern TimeT update_online;
+ extern pid_t pid;
+
+ extern Map<AccountId, accreg> accreg_db;
+
+ extern Map<PartyId, PartyMost> party_db;
+ extern PartyId party_newid;
+
+ extern Map<AccountId, Storage> storage_db;
+ } // namespace char_
+} // namespace tmwa
diff --git a/src/char/int_party.cpp b/src/char/int_party.cpp
index c9ab5a4..5ee65ad 100644
--- a/src/char/int_party.cpp
+++ b/src/char/int_party.cpp
@@ -29,33 +29,32 @@
#include "../generic/db.hpp"
#include "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
#include "../io/lock.hpp"
#include "../io/read.hpp"
#include "../io/write.hpp"
-#include "../net/packets.hpp"
-
#include "../proto2/char-map.hpp"
-#include "../mmo/extract.hpp"
#include "../mmo/ids.hpp"
-#include "../mmo/mmo.hpp"
+
+#include "../high/extract_mmo.hpp"
+#include "../high/mmo.hpp"
+
+#include "../wire/packets.hpp"
#include "char.hpp"
+#include "globals.hpp"
#include "inter.hpp"
+#include "inter_conf.hpp"
#include "../poison.hpp"
namespace tmwa
{
-AString party_txt = "save/party.txt"_s;
-
-static
-Map<PartyId, PartyMost> party_db;
-static
-PartyId party_newid = wrap<PartyId>(100_u32);
-
+namespace char_
+{
static
void mapif_party_broken(PartyId party_id, int flag);
static
@@ -89,9 +88,10 @@ AString inter_party_tostr(PartyPair p)
return AString(str);
}
+} // namespace char_
static
-bool extract(XString str, PartyPair *pp)
+bool impl_extract(XString str, PartyPair *pp)
{
PartyPair& p = *pp;
@@ -129,6 +129,8 @@ bool extract(XString str, PartyPair *pp)
return true;
}
+namespace char_
+{
static
void party_check_deleted_init(PartyPair p)
{
@@ -153,7 +155,7 @@ void party_check_deleted_init(PartyPair p)
// パーティデータのロード
void inter_party_init(void)
{
- io::ReadFile in(party_txt);
+ io::ReadFile in(inter_conf.party_txt);
if (!in.is_open())
return;
@@ -171,19 +173,20 @@ void inter_party_init(void)
}
PartyMost pm;
- PartyPair pp;
- pp.party_most = &pm;
+ PartyPair pp{PartyId(), borrow(pm)};
if (extract(line, &pp) && pp.party_id)
{
if (party_newid < next(pp.party_id))
party_newid = next(pp.party_id);
party_check_deleted_init(pp);
party_db.insert(pp.party_id, pm);
+ // note: this is still referring to the noncanonical copy of
+ // the PartyMost pointer. This is okay, though.
party_check_empty(pp);
}
else
{
- PRINTF("int_party: broken data [%s] line %d\n"_fmt, party_txt,
+ PRINTF("int_party: broken data [%s] line %d\n"_fmt, inter_conf.party_txt,
c + 1);
}
c++;
@@ -201,18 +204,16 @@ void inter_party_save_sub(PartyPair data, io::WriteFile& fp)
// パーティーデータのセーブ
int inter_party_save(void)
{
- io::WriteLock fp(party_txt);
+ io::WriteLock fp(inter_conf.party_txt);
if (!fp.is_open())
{
PRINTF("int_party: cant write [%s] !!! data is lost !!!\n"_fmt,
- party_txt);
+ inter_conf.party_txt);
return 1;
}
for (auto& pair : party_db)
{
- PartyPair tmp;
- tmp.party_id = pair.first;
- tmp.party_most = &pair.second;
+ PartyPair tmp{pair.first, borrow(pair.second)};
inter_party_save_sub(tmp, fp);
}
@@ -221,23 +222,21 @@ int inter_party_save(void)
// パーティ名検索用
static
-void search_partyname_sub(PartyPair p, PartyName str, PartyPair *dst)
+void search_partyname_sub(PartyPair p, PartyName str, Borrowed<Option<PartyPair>> dst)
{
if (p->name == str)
- *dst = p;
+ *dst = Some(p);
}
// パーティ名検索
static
-PartyPair search_partyname(PartyName str)
+Option<PartyPair> search_partyname(PartyName str)
{
- PartyPair p;
+ Option<PartyPair> p = None;
for (auto& pair : party_db)
{
- PartyPair tmp;
- tmp.party_id = pair.first;
- tmp.party_most = &pair.second;
- search_partyname_sub(tmp, str, &p);
+ PartyPair tmp{pair.first, borrow(pair.second)};
+ search_partyname_sub(tmp, str, borrow(p));
}
return p;
@@ -262,11 +261,11 @@ int party_check_exp_share(PartyPair p)
}
}
- return (maxlv == 0 || maxlv - minlv <= party_share_level);
+ return (maxlv == 0 || maxlv - minlv <= inter_conf.party_share_level);
}
// パーティが空かどうかチェック
-int party_check_empty(PartyPair p)
+int party_check_empty(const PartyPair p)
{
int i;
@@ -313,9 +312,7 @@ void party_check_conflict(PartyId party_id, AccountId account_id, CharName nick)
{
for (auto& pair : party_db)
{
- PartyPair tmp;
- tmp.party_id = pair.first;
- tmp.party_most = &pair.second;
+ PartyPair tmp{pair.first, borrow(pair.second)};
party_check_conflict_sub(tmp,
party_id, account_id, nick);
}
@@ -326,23 +323,27 @@ void party_check_conflict(PartyId party_id, AccountId account_id, CharName nick)
// パーティ作成可否
static
-void mapif_party_created(Session *s, AccountId account_id, PartyPair p)
+void mapif_party_created(Session *s, AccountId account_id, Option<PartyPair> p_)
{
Packet_Fixed<0x3820> fixed_20;
fixed_20.account_id = account_id;
- if (p)
- {
- fixed_20.error = 0;
- fixed_20.party_id = p.party_id;
- fixed_20.party_name = p->name;
- PRINTF("int_party: created! %d %s\n"_fmt, p.party_id, p->name);
- }
- else
+ OMATCH_BEGIN (p_)
{
- fixed_20.error = 1;
- fixed_20.party_id = PartyId();
- fixed_20.party_name = stringish<PartyName>("error"_s);
+ OMATCH_CASE_SOME (p)
+ {
+ fixed_20.error = 0;
+ fixed_20.party_id = p.party_id;
+ fixed_20.party_name = p->name;
+ PRINTF("int_party: created! %d %s\n"_fmt, p.party_id, p->name);
+ }
+ OMATCH_CASE_NONE ()
+ {
+ fixed_20.error = 1;
+ fixed_20.party_id = PartyId();
+ fixed_20.party_name = stringish<PartyName>("error"_s);
+ }
}
+ OMATCH_END ();
send_fpacket<0x3820, 35>(s, fixed_20);
}
@@ -359,7 +360,7 @@ void mapif_party_noinfo(Session *s, PartyId party_id)
// パーティ情報まとめ送り
static
-void mapif_party_info(Session *s, PartyPair p)
+void mapif_party_info(Session *s, const PartyPair p)
{
Packet_Head<0x3821> head_21;
head_21.party_id = p.party_id;
@@ -490,22 +491,21 @@ void mapif_parse_CreateParty(Session *s, AccountId account_id, PartyName name, C
if (!name.is_print())
{
PRINTF("int_party: illegal party name [%s]\n"_fmt, name);
- mapif_party_created(s, account_id, PartyPair());
+ mapif_party_created(s, account_id, None);
return;
}
}
- if (search_partyname(name))
+ if (search_partyname(name).is_some())
{
PRINTF("int_party: same name party exists [%s]\n"_fmt, name);
- mapif_party_created(s, account_id, PartyPair());
+ mapif_party_created(s, account_id, None);
return;
}
+
PartyMost p {};
- PartyPair pp;
- pp.party_most = &p;
party_newid = next(party_newid);
- pp.party_id = party_newid;
+ PartyPair pp{party_newid, borrow(p)};
p.name = name;
p.exp = 0;
p.item = 0;
@@ -518,7 +518,8 @@ void mapif_parse_CreateParty(Session *s, AccountId account_id, PartyName name, C
party_db.insert(pp.party_id, p);
- mapif_party_created(s, account_id, pp);
+ // pointer to noncanonical version
+ mapif_party_created(s, account_id, Some(pp));
mapif_party_info(s, pp);
}
@@ -526,12 +527,19 @@ void mapif_parse_CreateParty(Session *s, AccountId account_id, PartyName name, C
static
void mapif_parse_PartyInfo(Session *s, PartyId party_id)
{
- PartyPair p;
- p.party_most = party_db.search(party_id);
- if (p)
- mapif_party_info(s, p);
- else
- mapif_party_noinfo(s, party_id);
+ Option<P<PartyMost>> maybe_party_most = party_db.search(party_id);
+ OMATCH_BEGIN (maybe_party_most)
+ {
+ OMATCH_CASE_SOME (party_most)
+ {
+ mapif_party_info(s, PartyPair{party_id, party_most});
+ }
+ OMATCH_CASE_NONE ()
+ {
+ mapif_party_noinfo(s, party_id);
+ }
+ }
+ OMATCH_END ();
}
// パーティ追加要求
@@ -539,13 +547,13 @@ static
void mapif_parse_PartyAddMember(Session *s, PartyId party_id, AccountId account_id,
CharName nick, MapName map, int lv)
{
- PartyPair p;
- p.party_most = party_db.search(party_id);
- if (!p)
- {
- mapif_party_memberadded(s, party_id, account_id, 1);
- return;
- }
+ Option<P<PartyMost>> maybe_party_most = party_db.search(party_id);
+ P<PartyMost> party_most = TRY_UNWRAP(maybe_party_most,
+ {
+ mapif_party_memberadded(s, party_id, account_id, 1);
+ return;
+ });
+ PartyPair p{party_id, party_most};
for (int i = 0; i < MAX_PARTY; i++)
{
@@ -580,10 +588,7 @@ static
void mapif_parse_PartyChangeOption(Session *s, PartyId party_id, AccountId account_id,
int exp, int item)
{
- PartyPair p;
- p.party_most = party_db.search(party_id);
- if (!p)
- return;
+ PartyPair p{party_id, TRY_UNWRAP(party_db.search(party_id), return)};
p->exp = exp;
int flag = 0;
@@ -601,10 +606,8 @@ void mapif_parse_PartyChangeOption(Session *s, PartyId party_id, AccountId accou
// パーティ脱退要求
void mapif_parse_PartyLeave(Session *, PartyId party_id, AccountId account_id)
{
- PartyPair p;
- p.party_most = party_db.search(party_id);
- if (!p)
- return;
+ PartyPair p{party_id, TRY_UNWRAP(party_db.search(party_id), return)};
+
for (int i = 0; i < MAX_PARTY; i++)
{
if (p->member[i].account_id != account_id)
@@ -623,10 +626,7 @@ static
void mapif_parse_PartyChangeMap(Session *s, PartyId party_id, AccountId account_id,
MapName map, int online, int lv)
{
- PartyPair p;
- p.party_most = party_db.search(party_id);
- if (!p)
- return;
+ PartyPair p{party_id, TRY_UNWRAP(party_db.search(party_id), return)};
for (int i = 0; i < MAX_PARTY; i++)
{
@@ -822,4 +822,5 @@ void inter_party_leave(PartyId party_id, AccountId account_id)
{
mapif_parse_PartyLeave(nullptr, party_id, account_id);
}
+} // namespace char_
} // namespace tmwa
diff --git a/src/char/int_party.hpp b/src/char/int_party.hpp
index f33fc0d..d66afbf 100644
--- a/src/char/int_party.hpp
+++ b/src/char/int_party.hpp
@@ -22,21 +22,16 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
-#include "../net/fwd.hpp"
-
-#include "../mmo/fwd.hpp"
-
namespace tmwa
{
+namespace char_
+{
void inter_party_init(void);
int inter_party_save(void);
RecvResult inter_party_parse_frommap(Session *ms, uint16_t);
void inter_party_leave(PartyId party_id, AccountId account_id);
-
-extern AString party_txt;
+} // namespace char_
} // namespace tmwa
diff --git a/src/char/int_storage.cpp b/src/char/int_storage.cpp
index 76eff34..32af231 100644
--- a/src/char/int_storage.cpp
+++ b/src/char/int_storage.cpp
@@ -28,30 +28,30 @@
#include "../generic/db.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
+#include "../io/extract.hpp"
#include "../io/lock.hpp"
#include "../io/read.hpp"
#include "../io/write.hpp"
-#include "../net/packets.hpp"
-
#include "../proto2/char-map.hpp"
-#include "../mmo/extract.hpp"
-#include "../mmo/mmo.hpp"
+#include "../mmo/cxxstdio_enums.hpp"
+
+#include "../high/extract_mmo.hpp"
+#include "../high/mmo.hpp"
+
+#include "../wire/packets.hpp"
+
+#include "globals.hpp"
+#include "inter_conf.hpp"
#include "../poison.hpp"
namespace tmwa
{
-// ファイル名のデフォルト
-// inter_config_read()で再設定される
-AString storage_txt = "save/storage.txt"_s;
-
-static
-Map<AccountId, Storage> storage_db;
-
+namespace char_
+{
// 倉庫データを文字列に変換
static
AString storage_tostr(Storage *p)
@@ -90,10 +90,11 @@ AString storage_tostr(Storage *p)
return AString();
return AString(str);
}
+} // namespace char_
// 文字列を倉庫データに変換
static
-bool extract(XString str, Storage *p)
+bool impl_extract(XString str, Storage *p)
{
std::vector<Item> storage_items;
if (!extract(str,
@@ -116,15 +117,13 @@ bool extract(XString str, Storage *p)
return true;
}
+namespace char_
+{
// アカウントから倉庫データインデックスを得る(新規倉庫追加可能)
-Storage *account2storage(AccountId account_id)
+Borrowed<Storage> account2storage(AccountId account_id)
{
- Storage *s = storage_db.search(account_id);
- if (s == nullptr)
- {
- s = storage_db.init(account_id);
- s->account_id = account_id;
- }
+ P<Storage> s = storage_db.init(account_id);
+ s->account_id = account_id;
return s;
}
@@ -134,10 +133,10 @@ void inter_storage_init(void)
{
int c = 0;
- io::ReadFile in(storage_txt);
+ io::ReadFile in(inter_conf.storage_txt);
if (!in.is_open())
{
- PRINTF("cant't read : %s\n"_fmt, storage_txt);
+ PRINTF("cant't read : %s\n"_fmt, inter_conf.storage_txt);
return;
}
@@ -152,7 +151,7 @@ void inter_storage_init(void)
else
{
PRINTF("int_storage: broken data [%s] line %d\n"_fmt,
- storage_txt, c);
+ inter_conf.storage_txt, c);
}
c++;
}
@@ -170,12 +169,12 @@ void inter_storage_save_sub(Storage *data, io::WriteFile& fp)
// 倉庫データを書き込む
int inter_storage_save(void)
{
- io::WriteLock fp(storage_txt);
+ io::WriteLock fp(inter_conf.storage_txt);
if (!fp.is_open())
{
PRINTF("int_storage: cant write [%s] !!! data is lost !!!\n"_fmt,
- storage_txt);
+ inter_conf.storage_txt);
return 1;
}
for (auto& pair : storage_db)
@@ -196,7 +195,7 @@ void inter_storage_delete(AccountId account_id)
static
void mapif_load_storage(Session *ss, AccountId account_id)
{
- Storage *st = account2storage(account_id);
+ P<Storage> st = account2storage(account_id);
Packet_Payload<0x3810> payload_10;
payload_10.account_id = account_id;
payload_10.storage = *st;
@@ -240,11 +239,10 @@ RecvResult mapif_parse_SaveStorage(Session *ss)
if (rv != RecvResult::Complete)
return rv;
- Storage *st;
AccountId account_id = payload.account_id;
{
- st = account2storage(account_id);
+ P<Storage> st = account2storage(account_id);
*st = payload.storage;
mapif_save_storage_ack(ss, account_id);
}
@@ -273,4 +271,5 @@ RecvResult inter_storage_parse_frommap(Session *ms, uint16_t packet_id)
}
return rv;
}
+} // namespace char_
} // namespace tmwa
diff --git a/src/char/int_storage.hpp b/src/char/int_storage.hpp
index b8ec9db..3741061 100644
--- a/src/char/int_storage.hpp
+++ b/src/char/int_storage.hpp
@@ -22,21 +22,16 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
-#include "../net/fwd.hpp"
-
-#include "../mmo/fwd.hpp"
-
namespace tmwa
{
+namespace char_
+{
void inter_storage_init(void);
int inter_storage_save(void);
void inter_storage_delete(AccountId account_id);
-Storage *account2storage(AccountId account_id);
+Borrowed<Storage> account2storage(AccountId account_id);
RecvResult inter_storage_parse_frommap(Session *ms, uint16_t);
-
-extern AString storage_txt;
+} // namespace char_
} // namespace tmwa
diff --git a/src/char/inter.cpp b/src/char/inter.cpp
index f757991..3bf3bfc 100644
--- a/src/char/inter.cpp
+++ b/src/char/inter.cpp
@@ -34,18 +34,24 @@
#include "../generic/db.hpp"
#include "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
#include "../io/lock.hpp"
#include "../io/read.hpp"
+#include "../io/span.hpp"
#include "../io/write.hpp"
-#include "../net/packets.hpp"
+#include "../mmo/config_parse.hpp"
#include "../proto2/char-map.hpp"
-#include "../mmo/extract.hpp"
-#include "../mmo/mmo.hpp"
+#include "../high/extract_mmo.hpp"
+#include "../high/mmo.hpp"
+
+#include "../wire/packets.hpp"
#include "char.hpp"
+#include "globals.hpp"
+#include "inter_conf.hpp"
#include "int_party.hpp"
#include "int_storage.hpp"
@@ -54,20 +60,8 @@
namespace tmwa
{
-static
-AString accreg_txt = "save/accreg.txt"_s;
-
-struct accreg
+namespace char_
{
- AccountId account_id;
- int reg_num;
- Array<GlobalReg, ACCOUNT_REG_NUM> reg;
-};
-static
-Map<AccountId, struct accreg> accreg_db;
-
-int party_share_level = 10;
-
//--------------------------------------------------------
// アカウント変数を文字列へ変換
@@ -84,7 +78,7 @@ AString inter_accreg_tostr(struct accreg *reg)
// アカウント変数を文字列から変換
static
-bool extract(XString str, struct accreg *reg)
+bool impl_extract(XString str, struct accreg *reg)
{
std::vector<GlobalReg> vars;
if (!extract(str,
@@ -108,7 +102,7 @@ void inter_accreg_init(void)
{
int c = 0;
- io::ReadFile in(accreg_txt);
+ io::ReadFile in(inter_conf.accreg_txt);
if (!in.is_open())
return;
AString line;
@@ -121,7 +115,7 @@ void inter_accreg_init(void)
}
else
{
- PRINTF("inter: accreg: broken data [%s] line %d\n"_fmt, accreg_txt,
+ PRINTF("inter: accreg: broken data [%s] line %d\n"_fmt, inter_conf.accreg_txt,
c);
}
c++;
@@ -143,11 +137,11 @@ void inter_accreg_save_sub(struct accreg *reg, io::WriteFile& fp)
static
int inter_accreg_save(void)
{
- io::WriteLock fp(accreg_txt);
+ io::WriteLock fp(inter_conf.accreg_txt);
if (!fp.is_open())
{
PRINTF("int_accreg: cant write [%s] !!! data is lost !!!\n"_fmt,
- accreg_txt);
+ inter_conf.accreg_txt);
return 1;
}
for (auto& pair : accreg_db)
@@ -156,36 +150,6 @@ int inter_accreg_save(void)
return 0;
}
-bool inter_config(XString w1, ZString w2)
-{
- {
- if (w1 == "storage_txt"_s)
- {
- storage_txt = w2;
- }
- else if (w1 == "party_txt"_s)
- {
- party_txt = w2;
- }
- else if (w1 == "accreg_txt"_s)
- {
- accreg_txt = w2;
- }
- else if (w1 == "party_share_level"_s)
- {
- party_share_level = atoi(w2.c_str());
- if (party_share_level < 0)
- party_share_level = 0;
- }
- else
- {
- return false;
- }
- }
-
- return true;
-}
-
// セーブ
void inter_save(void)
{
@@ -264,12 +228,12 @@ void mapif_account_reg(Session *s, AccountId account_id, const std::vector<Packe
static
void mapif_account_reg_reply(Session *s, AccountId account_id)
{
- struct accreg *reg = accreg_db.search(account_id);
+ Option<P<struct accreg>> reg_ = accreg_db.search(account_id);
Packet_Head<0x3804> head_04;
head_04.account_id = account_id;
std::vector<Packet_Repeat<0x3804>> repeat_04;
- if (reg)
+ OMATCH_BEGIN_SOME (reg, reg_)
{
repeat_04.resize(reg->reg_num);
assert (reg->reg_num < ACCOUNT_REG_NUM);
@@ -279,6 +243,7 @@ void mapif_account_reg_reply(Session *s, AccountId account_id)
repeat_04[j].value = reg->reg[j].value;
}
}
+ OMATCH_END ();
send_vpacket<0x3804, 8, 36>(s, head_04, repeat_04);
}
@@ -408,13 +373,9 @@ RecvResult mapif_parse_AccReg(Session *s)
if (rv != RecvResult::Complete)
return rv;
- struct accreg *reg = accreg_db.search(head.account_id);
-
- if (reg == nullptr)
+ P<struct accreg> reg = accreg_db.init(head.account_id);
{
- AccountId account_id = head.account_id;
- reg = accreg_db.init(account_id);
- reg->account_id = account_id;
+ reg->account_id = head.account_id;
}
size_t jlim = std::min(repeat.size(), ACCOUNT_REG_NUM);
@@ -488,4 +449,5 @@ RecvResult inter_parse_frommap(Session *ms, uint16_t packet_id)
return rv;
}
+} // namespace char_
} // namespace tmwa
diff --git a/src/char/inter.hpp b/src/char/inter.hpp
index 19900f9..c641254 100644
--- a/src/char/inter.hpp
+++ b/src/char/inter.hpp
@@ -22,17 +22,27 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
+#include "../generic/array.hpp"
-#include "../net/fwd.hpp"
+#include "../mmo/consts.hpp"
+#include "../mmo/ids.hpp"
+
+#include "../proto2/net-GlobalReg.hpp"
namespace tmwa
{
-bool inter_config(XString key, ZString value);
+namespace char_
+{
+struct accreg
+{
+ AccountId account_id;
+ int reg_num;
+ Array<GlobalReg, ACCOUNT_REG_NUM> reg;
+};
+
void inter_init2();
void inter_save(void);
RecvResult inter_parse_frommap(Session *ms, uint16_t packet_id);
-
-extern int party_share_level;
+} // namespace char_
} // namespace tmwa
diff --git a/src/char/main.cpp b/src/char/main.cpp
index 3648a74..7d6fee3 100644
--- a/src/char/main.cpp
+++ b/src/char/main.cpp
@@ -17,7 +17,7 @@
// 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 "../mmo/core.hpp"
+#include "../high/core.hpp"
#include "char.hpp"
diff --git a/src/compat/attr.hpp b/src/compat/attr.hpp
index 238a5d5..5ebef6d 100644
--- a/src/compat/attr.hpp
+++ b/src/compat/attr.hpp
@@ -1,7 +1,7 @@
#pragma once
// attr.hpp - Attributes.
//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2013-2014 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -28,4 +28,6 @@ namespace tmwa
#else
# define FALLTHROUGH /* fallthrough */
#endif
+
+#define JOIN(a, b) a##b
} // namespace tmwa
diff --git a/src/compat/borrow.hpp b/src/compat/borrow.hpp
new file mode 100644
index 0000000..0ea6a26
--- /dev/null
+++ b/src/compat/borrow.hpp
@@ -0,0 +1,111 @@
+#pragma once
+// borrow.hpp - a non-null, unowned, pointer
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+#include <cstdlib>
+
+#include <iterator>
+
+#include "option.hpp"
+
+// unit tests currention in option_test.cpp
+
+namespace tmwa
+{
+ // TODO see if const-by-default is a thing
+ template<class T>
+ class Borrowed
+ {
+ T *stupid;
+ public:
+ Borrowed() = delete;
+ explicit
+ Borrowed(T *p) : stupid(p)
+ {
+ if (!p) abort();
+ }
+
+ T& operator *() const
+ {
+ return *stupid;
+ }
+
+ T *operator ->() const
+ {
+ return stupid;
+ }
+
+ template<class U>
+ Borrowed<U> downcast_to() const
+ {
+ static_assert(std::is_base_of<T, U>::value, "base check");
+ static_assert(!std::is_same<T, U>::value, "same check");
+ return Borrowed<U>(static_cast<U *>(stupid));
+ }
+
+ template<class U>
+ Borrowed<U> upcast_to() const
+ {
+ static_assert(std::is_base_of<U, T>::value, "base check");
+ static_assert(!std::is_same<T, U>::value, "same check");
+ return Borrowed<U>(stupid);
+ }
+
+ friend bool operator == (Borrowed l, Borrowed r)
+ {
+ return l.stupid == r.stupid;
+ }
+ friend bool operator != (Borrowed l, Borrowed r)
+ {
+ return !(l == r);
+ }
+ };
+
+ namespace option
+ {
+ template<class T>
+ class OptionRepr<Borrowed<T>>
+ {
+ T *stupider;
+ public:
+ void set_none() { stupider = nullptr; }
+ void post_set_some() {}
+ bool is_some() const { return stupider != nullptr; }
+ Borrowed<T> *ptr() { return reinterpret_cast<Borrowed<T> *>(&stupider); }
+ const Borrowed<T> *ptr() const { return reinterpret_cast<const Borrowed<T> *>(&stupider); }
+ };
+ }
+
+ template<class T>
+ using P = Borrowed<T>;
+
+ template<class T>
+ Borrowed<T> borrow(T& ref)
+ {
+ return Borrowed<T>(&ref);
+ }
+
+ template<class T>
+ T *as_raw_pointer(Option<Borrowed<T>> ptr)
+ {
+ return &*TRY_UNWRAP(ptr, return nullptr);
+ }
+} // namespace tmwa
diff --git a/src/compat/borrow.py b/src/compat/borrow.py
new file mode 100644
index 0000000..58cd19b
--- /dev/null
+++ b/src/compat/borrow.py
@@ -0,0 +1,18 @@
+class Borrowed(object):
+ __slots__ = ('_value')
+ name = 'tmwa::Borrowed'
+ enabled = True
+
+ def __init__(self, value):
+ self._value = value['stupid']
+
+ def to_string(self):
+ return self._value
+
+ test_extra = '''
+ static int borrow_thingy;
+ '''
+
+ tests = [
+ ('tmwa::borrow(borrow_thingy)', '<borrow_thingy>'),
+ ]
diff --git a/src/compat/fwd.hpp b/src/compat/fwd.hpp
index 45f3c24..3fa0dd2 100644
--- a/src/compat/fwd.hpp
+++ b/src/compat/fwd.hpp
@@ -20,8 +20,23 @@
#include "../sanity.hpp"
+#include "../ints/fwd.hpp" // rank 1
+#include "../strings/fwd.hpp" // rank 1
+// compat/fwd.hpp is rank 2
+
namespace tmwa
{
+ namespace option
+ {
+ template<class T>
+ class Option;
+ }
+ using option::Option;
+
+ template<class T>
+ class Borrowed;
+
+ struct TimeT;
// meh, add more when I feel like it
} // namespace tmwa
diff --git a/src/compat/nullpo.hpp b/src/compat/nullpo.hpp
index 5be674a..38c8e92 100644
--- a/src/compat/nullpo.hpp
+++ b/src/compat/nullpo.hpp
@@ -52,13 +52,15 @@ bool nullpo_chk(const char *file, int line, const char *func,
const void *target);
template<class T>
-bool nullpo_chk(const char *file, int line, const char *func, T target)
-{
- return nullpo_chk(file, line, func, target.operator->());
-}
+bool nullpo_chk(const char *, int, const char *, Borrowed<T>) = delete;
template<class T>
bool nullpo_chk(const char *file, int line, const char *func, T *target)
{
return nullpo_chk(file, line, func, static_cast<const void *>(target));
}
+template<class T>
+bool nullpo_chk(const char *file, int line, const char *func, T target)
+{
+ return nullpo_chk(file, line, func, target.operator->());
+}
} // namespace tmwa
diff --git a/src/generic/operators.hpp b/src/compat/operators.hpp
index bb05765..bb05765 100644
--- a/src/generic/operators.hpp
+++ b/src/compat/operators.hpp
diff --git a/src/compat/option.hpp b/src/compat/option.hpp
new file mode 100644
index 0000000..b6e7655
--- /dev/null
+++ b/src/compat/option.hpp
@@ -0,0 +1,469 @@
+#pragma once
+// option.hpp - a data type that may or may not exist
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+#include <cassert>
+
+#include <utility>
+
+
+namespace tmwa
+{
+namespace option
+{
+ enum option_hack_type { option_hack_value };
+
+ template<class T>
+ class OptionRepr
+ {
+ __attribute__((aligned(alignof(T))))
+ char _data[sizeof(T)];
+ bool _some;
+ public:
+ void set_none() { _some = false; }
+ // maybe add pre_set_some if it is useful for other specializations
+ void post_set_some() { _some = true; }
+ bool is_some() const { return _some; }
+ T *ptr() { return reinterpret_cast<T *>(&_data); }
+ const T *ptr() const { return reinterpret_cast<const T *>(&_data); }
+ };
+ template<class T>
+ class OptionRepr<T&>;
+ template<class T>
+ class OptionRepr<T&&>;
+
+ template<class T>
+ Option<T> None(option_hack_type=option_hack_value)
+ {
+ return None;
+ }
+
+ template<class T>
+ Option<T> Some(T v)
+ {
+ Option<T> rv = None;
+ rv.do_construct(std::move(v));
+ return rv;
+ }
+
+ // TODO all *_or and *_set methods should have a lazy version too
+ template<class T>
+ class Option
+ {
+ static_assert(std::is_pod<OptionRepr<T>>::value, "repr should itself be pod, copies are done manually");
+ OptionRepr<T> repr;
+
+ friend Option<T> Some<T>(T);
+
+ void do_init()
+ {
+ repr.set_none();
+ }
+ template<class... U>
+ void do_construct(U&&... u)
+ {
+ new(repr.ptr()) T(std::forward<U>(u)...);
+ repr.post_set_some();
+ }
+ void do_move_construct(Option&& r)
+ {
+ if (r.repr.is_some())
+ {
+ do_construct(std::move(*r.repr.ptr()));
+ r.do_destruct();
+ }
+ }
+ void do_copy_construct(const Option& r)
+ {
+ if (r.repr.is_some())
+ {
+ do_construct(*r.repr.ptr());
+ }
+ }
+ void do_move_assign(Option&& r)
+ {
+ if (repr.is_some())
+ {
+ if (r.repr.is_some())
+ {
+ *repr.ptr() = std::move(*r.repr.ptr());
+ }
+ else
+ {
+ do_destruct();
+ }
+ return;
+ }
+ else
+ {
+ do_move_construct(std::move(r));
+ }
+ }
+ void do_copy_assign(const Option& r)
+ {
+ if (repr.is_some())
+ {
+ if (r.repr.is_some())
+ {
+ *repr.ptr() = *r.repr.ptr();
+ }
+ else
+ {
+ do_destruct();
+ }
+ return;
+ }
+ else
+ {
+ do_copy_construct(r);
+ }
+ }
+ void do_destruct()
+ {
+ repr.ptr()->~T();
+ repr.set_none();
+ }
+ public:
+ Option() = delete;
+ Option(Option(*)(option_hack_type))
+ {
+ do_init();
+ }
+ Option(Option&& r)
+ {
+ do_init();
+ do_move_construct(std::move(r));
+ }
+ Option(const Option& r)
+ {
+ do_init();
+ do_copy_construct(r);
+ }
+ Option& operator = (Option&& r)
+ {
+ do_move_assign(std::move(r));
+ return *this;
+ }
+ Option& operator = (const Option& r)
+ {
+ do_copy_assign(r);
+ return *this;
+ }
+ ~Option()
+ {
+ if (repr.is_some())
+ {
+ do_destruct();
+ }
+ }
+
+ T move_or(T def)
+ {
+ if (repr.is_some())
+ {
+ def = std::move(*repr.ptr());
+ do_destruct();
+ }
+ return def;
+ }
+ T copy_or(T def) const
+ {
+ if (repr.is_some())
+ {
+ def = *repr.ptr();
+ }
+ return def;
+ }
+ T& ref_or(T& def)
+ {
+ return repr.is_some() ? *repr.ptr() : def;
+ }
+ const T& ref_or(const T& def) const
+ {
+ return repr.is_some() ? *repr.ptr() : def;
+ }
+ T *ptr_or(T *def)
+ {
+ return repr.is_some() ? repr.ptr() : def;
+ }
+ const T *ptr_or(const T *def) const
+ {
+ return repr.is_some() ? repr.ptr() : def;
+ }
+ bool is_some() const
+ {
+ return repr.is_some();
+ }
+ bool is_none() const
+ {
+ return !is_some();
+ }
+
+ template<class F>
+ auto move_map(F&& f) -> Option<decltype(std::forward<F>(f)(std::move(*repr.ptr())))>
+ {
+ if (repr.is_some())
+ {
+ auto rv = Some(std::forward<F>(f)(std::move(*repr.ptr())));
+ do_destruct();
+ return rv;
+ }
+ else
+ {
+ return None;
+ }
+ }
+ template<class F>
+ auto map(F&& f) -> Option<decltype(std::forward<F>(f)(*repr.ptr()))>
+ {
+ if (repr.is_some())
+ {
+ return Some(std::forward<F>(f)(*repr.ptr()));
+ }
+ else
+ {
+ return None;
+ }
+ }
+ template<class F>
+ auto map(F&& f) const -> Option<decltype(std::forward<F>(f)(*repr.ptr()))>
+ {
+ if (repr.is_some())
+ {
+ return Some(std::forward<F>(f)(*repr.ptr()));
+ }
+ else
+ {
+ return None;
+ }
+ }
+ // shortcut for flatten(o.map()) that avoids explicit Some's inside
+ template<class B, class F>
+ auto cmap(B&& b, F&& f) const -> Option<decltype(std::forward<F>(f)(*repr.ptr()))>
+ {
+ if (repr.is_some() && std::forward<B>(b)(*repr.ptr()))
+ {
+ return Some(std::forward<F>(f)(*repr.ptr()));
+ }
+ else
+ {
+ return None;
+ }
+ }
+ // wanting members is *so* common
+ template<class M, class B>
+ Option<M> pmd_get(const M B::*pmd) const
+ {
+ if (repr.is_some())
+ {
+ return Some((*repr.ptr()).*pmd);
+ }
+ else
+ {
+ return None;
+ }
+ }
+ template<class M, class B>
+ void pmd_set(M B::*pmd, M value)
+ {
+ if (repr.is_some())
+ {
+ ((*repr.ptr()).*pmd) = std::move(value);
+ }
+ }
+ template<class M, class B>
+ Option<M> pmd_pget(const M B::*pmd) const
+ {
+ if (repr.is_some())
+ {
+ return Some((**repr.ptr()).*pmd);
+ }
+ else
+ {
+ return None;
+ }
+ }
+ template<class M, class B>
+ void pmd_pset(M B::*pmd, M value)
+ {
+ if (repr.is_some())
+ {
+ ((**repr.ptr()).*pmd) = std::move(value);
+ }
+ }
+ };
+
+ template<class T>
+ struct most_flattened_type
+ {
+ using type = T;
+
+ static Option<type> flatten(Option<T> o)
+ {
+ return std::move(o);
+ }
+ };
+ template<class T>
+ struct most_flattened_type<Option<T>>
+ {
+ using type = typename most_flattened_type<T>::type;
+
+ static Option<type> flatten(Option<Option<T>> o)
+ {
+ return most_flattened_type<T>::flatten(o.move_or(None));
+ }
+ };
+
+ template<class T>
+ Option<typename most_flattened_type<T>::type> flatten(Option<T> o)
+ {
+ return most_flattened_type<T>::flatten(std::move(o));
+ }
+
+ template<class T>
+ bool operator == (const Option<T>& l, const Option<T>& r)
+ {
+ const T *l2 = l.ptr_or(nullptr);
+ const T *r2 = r.ptr_or(nullptr);
+ if (!l2 && !r2)
+ return true;
+ if (l2 && r2)
+ {
+ return *l2 == *r2;
+ }
+ return false;
+ }
+ template<class T>
+ bool operator != (const Option<T>& l, const Option<T>& r)
+ {
+ return !(l == r);
+ }
+ template<class T>
+ bool operator < (const Option<T>& l, const Option<T>& r)
+ {
+ const T *l2 = l.ptr_or(nullptr);
+ const T *r2 = r.ptr_or(nullptr);
+
+ if (!l2 && r2)
+ return true;
+ if (l2 && r2)
+ {
+ return *l2 < *r2;
+ }
+ return false;
+ }
+ template<class T>
+ bool operator > (const Option<T>& l, const Option<T>& r)
+ {
+ return (r < l);
+ }
+ template<class T>
+ bool operator <= (const Option<T>& l, const Option<T>& r)
+ {
+ return !(r < l);
+ }
+ template<class T>
+ bool operator >= (const Option<T>& l, const Option<T>& r)
+ {
+ return !(l < r);
+ }
+
+ // workaround for the fact that most references can't escape
+ template<class T>
+ struct RefWrapper
+ {
+ T maybe_ref;
+
+ T maybe_ref_fun() { return std::forward<T>(maybe_ref); }
+ };
+
+ template<class T>
+ RefWrapper<T> option_unwrap(RefWrapper<Option<T>> o)
+ { return RefWrapper<T>{std::move(*reinterpret_cast<OptionRepr<T>&>(o.maybe_ref).ptr())}; }
+ template<class T>
+ RefWrapper<T&> option_unwrap(RefWrapper<Option<T>&> o)
+ { return RefWrapper<T&>{*reinterpret_cast<OptionRepr<T>&>(o.maybe_ref).ptr()}; }
+ template<class T>
+ RefWrapper<T&&> option_unwrap(RefWrapper<Option<T>&&> o)
+ { return RefWrapper<T&&>{std::move(*reinterpret_cast<OptionRepr<T>&>(o.maybe_ref).ptr())}; }
+ template<class T>
+ RefWrapper<T> option_unwrap(RefWrapper<const Option<T>> o)
+ { return RefWrapper<T>{std::move(*reinterpret_cast<const OptionRepr<T>&>(o.maybe_ref).ptr())}; }
+ template<class T>
+ RefWrapper<const T&> option_unwrap(RefWrapper<const Option<T>&> o)
+ { return RefWrapper<const T&>{*reinterpret_cast<const OptionRepr<T>&>(o.maybe_ref).ptr()}; }
+ template<class T>
+ RefWrapper<const T&&> option_unwrap(RefWrapper<const Option<T>&&> o)
+ { return RefWrapper<const T&&>{std::move(*reinterpret_cast<const OptionRepr<T>&>(o.maybe_ref).ptr())}; }
+
+ // if you think you understand this, you're not trying hard enough.
+#define TRY_UNWRAP(opt, falsy) \
+ ({ \
+ tmwa::option::RefWrapper<decltype((opt))> o = {(opt)}; \
+ if (o.maybe_ref.is_none()) falsy; \
+ tmwa::option::option_unwrap(std::move(o)); \
+ }).maybe_ref_fun()
+
+#define OMATCH_BEGIN(expr) \
+ { \
+ auto&& _omatch_var = (expr); \
+ switch (_omatch_var.is_some()) \
+ { \
+ { \
+ { \
+ /*}}}}*/
+#define OMATCH_END() \
+ /*{{{{*/ \
+ } \
+ } \
+ } \
+ (void) _omatch_var; \
+ }
+
+#define OMATCH_BEGIN_SOME(var, expr) \
+ OMATCH_BEGIN (expr) \
+ OMATCH_CASE_SOME (var)
+
+#define OMATCH_CASE_SOME(var) \
+ /*{{{{*/ \
+ } \
+ break; \
+ } \
+ { \
+ case true: \
+ { \
+ auto&& var = *_omatch_var.ptr_or(nullptr); \
+ /*}}}}*/
+#define OMATCH_CASE_NONE() \
+ /*{{{{*/ \
+ } \
+ break; \
+ } \
+ { \
+ case false: \
+ { \
+ /*}}}}*/
+} // namespace option
+
+//using option::Option;
+using option::None;
+using option::Some;
+} // namespace tmwa
diff --git a/src/compat/option.py b/src/compat/option.py
new file mode 100644
index 0000000..7704174
--- /dev/null
+++ b/src/compat/option.py
@@ -0,0 +1,39 @@
+class Option(object):
+ __slots__ = ('_value')
+ name = 'tmwa::option::Option'
+ enabled = True
+
+ def __init__(self, value):
+ self._value = value['repr']
+
+ def to_string(self):
+ value = self._value
+ ty = value.type.template_argument(0)
+ try:
+ some = bool(value['_some'])
+ except gdb.error:
+ stupider = value['stupider']
+ if stupider:
+ return 'Some<%s>(%s)' % (ty, stupider)
+ else:
+ return 'None<%s>' % ty
+ else:
+ if some:
+ data = value['_data']
+ data = data.address.cast(ty.pointer()).dereference()
+ return 'Some<%s>(%s)' % (ty, data)
+ else:
+ return 'None<%s>' % ty
+
+ test_extra = '''
+ #include "../compat/borrow.hpp"
+
+ static int option_borrow_thingy;
+ '''
+
+ tests = [
+ ('tmwa::None<int>()', 'None<int>'),
+ ('tmwa::Some(1)', 'Some<int>(1)'),
+ ('tmwa::Option<tmwa::Borrowed<int>>(tmwa::None)', 'None<tmwa::Borrowed<int>>'),
+ ('tmwa::Some(tmwa::borrow(option_borrow_thingy))', 'Some<tmwa::Borrowed<int>>(<option_borrow_thingy>)'),
+ ]
diff --git a/src/compat/option_test.cpp b/src/compat/option_test.cpp
new file mode 100644
index 0000000..69f3a60
--- /dev/null
+++ b/src/compat/option_test.cpp
@@ -0,0 +1,499 @@
+#include "option.hpp"
+// option_test.cpp - Testsuite for a type that may or may not exist
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 <gtest/gtest.h>
+
+#include "../strings/literal.hpp"
+
+#include "borrow.hpp"
+
+#include "../diagnostics.hpp"
+//#include "../poison.hpp"
+
+
+namespace tmwa
+{
+TEST(Option, somenone)
+{
+ {
+ option::Option<int> opt = option::None;
+ opt = option::None;
+ }
+ {
+ option::Option<int> opt = option::None<int>;
+ opt = option::None<int>;
+ }
+ {
+ option::Option<int> opt = option::None<int>();
+ opt = option::None<int>();
+ }
+ {
+ option::Option<int> opt = option::Some(123);
+ opt = option::Some(123);
+ }
+ {
+ option::Option<int> opt = option::Some<int>(123);
+ opt = option::Some<int>(123);
+ }
+}
+TEST(Option, somenonenocopy)
+{
+ struct Foo
+ {
+ Foo() = default;
+ Foo(Foo&&) = default;
+ Foo(const Foo&) = delete;
+ Foo& operator = (Foo&&) = default;
+ Foo& operator = (const Foo&) = delete;
+ };
+ {
+ option::Option<Foo> opt = option::None;
+ opt = option::None;
+ }
+ // clang <= 3.4 is buggy
+ // since clang doesn't version, there is no way to restrict it to clang 3.5+
+#ifndef __clang__
+ {
+ option::Option<Foo> opt = option::None<Foo>;
+ opt = option::None<Foo>;
+ }
+#endif
+ {
+ option::Option<Foo> opt = option::None<Foo>();
+ opt = option::None<Foo>();
+ }
+ {
+ option::Option<Foo> opt = option::Some(Foo());
+ opt = option::Some(Foo());
+ }
+ {
+ option::Option<Foo> opt = option::Some<Foo>(Foo());
+ opt = option::Some<Foo>(Foo());
+ }
+}
+TEST(Option, customrepr)
+{
+ int iv = 123;
+ Borrowed<int> i = borrow(iv);
+
+ EXPECT_EQ(&iv, as_raw_pointer(Some(i)));
+
+ {
+ option::Option<Borrowed<int>> opt = option::None;
+ opt = option::None;
+ }
+ {
+ option::Option<Borrowed<int>> opt = option::None<Borrowed<int>>;
+ opt = option::None<Borrowed<int>>;
+ }
+ {
+ option::Option<Borrowed<int>> opt = option::None<Borrowed<int>>();
+ opt = option::None<Borrowed<int>>();
+ }
+ {
+ option::Option<Borrowed<int>> opt = option::Some(i);
+ opt = option::Some(i);
+ }
+ {
+ option::Option<Borrowed<int>> opt = option::Some<Borrowed<int>>(i);
+ opt = option::Some<Borrowed<int>>(i);
+ }
+}
+
+TEST(Option, destruct)
+{
+ struct BugCheck
+ {
+ bool *destroyed;
+
+ BugCheck(bool *d)
+ : destroyed(d)
+ {}
+ BugCheck(BugCheck&& r)
+ : destroyed(r.destroyed)
+ {
+ r.destroyed = nullptr;
+ }
+ BugCheck& operator = (BugCheck&& r)
+ {
+ std::swap(destroyed, r.destroyed);
+ return *this;
+ }
+ ~BugCheck()
+ {
+ if (!destroyed)
+ return;
+ if (*destroyed)
+ abort();
+ *destroyed = true;
+ }
+ };
+
+ bool destroyed = false;
+
+ Option<BugCheck> bug = Some(BugCheck(&destroyed));
+ bug = None;
+}
+
+TEST(Option, def)
+{
+ struct Tracked
+ {
+ int id;
+ int gen;
+
+ Tracked(int i, int g=0) : id(i), gen(g) {}
+ Tracked(Tracked&&) = default;
+ Tracked(const Tracked& r) : id(r.id), gen(r.gen + 1) {}
+ Tracked& operator = (Tracked&&) = default;
+ Tracked& operator = (const Tracked& r) { id = r.id; gen = r.gen + 1; return *this; }
+
+ bool operator == (const Tracked& r) const
+ {
+ return this->id == r.id && this->gen == r.gen;
+ }
+ bool operator != (const Tracked& r) const
+ {
+ return !(*this == r);
+ }
+ };
+
+ {
+ option::Option<Tracked> o = option::None;
+ EXPECT_EQ(o.move_or(Tracked(1)), Tracked(1));
+ EXPECT_EQ(o.copy_or(Tracked(2)), Tracked(2));
+ Tracked t3(3);
+ Tracked& r3 = o.ref_or(t3);
+ EXPECT_EQ(&r3, &t3);
+ Tracked t4(4);
+ Tracked *r4 = o.ptr_or(&t4);
+ EXPECT_EQ(r4, &t4);
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ }
+ {
+ const option::Option<Tracked> o = option::None;
+ EXPECT_EQ(o.copy_or(Tracked(2)), Tracked(2));
+ Tracked t3(3);
+ const Tracked& r3 = o.ref_or(t3);
+ EXPECT_EQ(&r3, &t3);
+ Tracked t4(4);
+ const Tracked *r4 = o.ptr_or(&t4);
+ EXPECT_EQ(r4, &t4);
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ }
+ {
+ option::Option<Tracked> o = option::Some(Tracked(0));
+ EXPECT_EQ(o.move_or(Tracked(1)), Tracked(0));
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ o = option::Some(Tracked(0));
+ EXPECT_EQ(o.copy_or(Tracked(2)), Tracked(0, 1));
+ Tracked t3(3);
+ Tracked& r3 = o.ref_or(t3);
+ EXPECT_NE(&r3, &t3);
+ Tracked t4(4);
+ Tracked *r4 = o.ptr_or(&t4);
+ EXPECT_NE(r4, &t4);
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(&r3, r4);
+ EXPECT_EQ(r4, reinterpret_cast<Tracked *>(&o));
+ }
+ {
+ const option::Option<Tracked> o = option::Some(Tracked(0));
+ EXPECT_EQ(o.copy_or(Tracked(2)), Tracked(0, 1));
+ Tracked t3(3);
+ const Tracked& r3 = o.ref_or(t3);
+ EXPECT_NE(&r3, &t3);
+ Tracked t4(4);
+ const Tracked *r4 = o.ptr_or(&t4);
+ EXPECT_NE(r4, &t4);
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(&r3, r4);
+ EXPECT_EQ(r4, reinterpret_cast<const Tracked *>(&o));
+ }
+}
+
+TEST(Option, map)
+{
+ struct Foo
+ {
+ Foo() = default;
+ Foo(Foo&&) = default;
+ Foo(const Foo&) = delete;
+ Foo& operator = (Foo&&) = default;
+ Foo& operator = (const Foo&) = delete;
+ };
+
+ // move
+ {
+ option::Option<Foo> o = option::None;
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.move_map([](Foo){ return 0; });
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.ptr_or(nullptr), nullptr);
+ }
+ {
+ option::Option<Foo> o = option::Some(Foo());
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.move_map([](Foo){ return 1; });
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ EXPECT_NE(i.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.copy_or(0), 1);
+ }
+ // mut ref
+ {
+ option::Option<Foo> o = option::None;
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.map([](Foo&){ return 0; });
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.ptr_or(nullptr), nullptr);
+ }
+ {
+ option::Option<Foo> o = option::Some(Foo());
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.map([](Foo&){ return 1; });
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ EXPECT_NE(i.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.copy_or(0), 1);
+ }
+ // const ref
+ {
+ option::Option<Foo> o = option::None;
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.map([](const Foo&){ return 0; });
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.ptr_or(nullptr), nullptr);
+ }
+ {
+ option::Option<Foo> o = option::Some(Foo());
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.map([](const Foo&){ return 1; });
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ EXPECT_NE(i.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.copy_or(0), 1);
+ }
+}
+
+TEST(Option, member)
+{
+ struct Foo
+ {
+ int bar = 404;
+ };
+
+ Option<Foo> vng = None;
+ EXPECT_EQ(vng.pmd_get(&Foo::bar).copy_or(42), 42);
+ Option<Foo> vsg = Some(Foo());
+ EXPECT_EQ(vsg.pmd_get(&Foo::bar).copy_or(42), 404);
+
+ Option<Foo> vns = None;
+ vns.pmd_set(&Foo::bar, 42);
+ EXPECT_EQ(vns.copy_or(Foo()).bar, 404);
+ Option<Foo> vss = Some(Foo());
+ vss.pmd_set(&Foo::bar, 42);
+ EXPECT_EQ(vss.copy_or(Foo()).bar, 42);
+
+ Foo foo, alt;
+
+ Option<P<Foo>> png = None;
+ EXPECT_EQ(png.pmd_pget(&Foo::bar).copy_or(42), 42);
+ Option<P<Foo>> psg = Some(borrow(foo));
+ EXPECT_EQ(psg.pmd_pget(&Foo::bar).copy_or(42), 404);
+
+ Option<P<Foo>> pns = None;
+ pns.pmd_pset(&Foo::bar, 42);
+ EXPECT_EQ(pns.copy_or(borrow(alt))->bar, 404);
+ EXPECT_EQ(foo.bar, 404);
+ Option<P<Foo>> pss = Some(borrow(foo));
+ pss.pmd_pset(&Foo::bar, 42);
+ EXPECT_EQ(pss.copy_or(borrow(alt))->bar, 42);
+ EXPECT_EQ(foo.bar, 42);
+ EXPECT_EQ(alt.bar, 404);
+}
+
+#if __cplusplus >= 201300 // c++14 as given by gcc 4.9
+# define DECLTYPE_AUTO decltype(auto)
+#else
+# define DECLTYPE_AUTO auto&&
+#endif
+
+TEST(Option, unwrap)
+{
+ int x;
+
+ Option<int> v = Some(1);
+ Option<int>& l = v;
+ Option<int>&& r = std::move(v);
+ const Option<int> cv = v;
+ // significantly, see the mut
+ const Option<int>& cl = v;
+ const Option<int>&& cr = std::move(v);
+
+ auto fv = [&]() -> Option<int> { return v; };
+ auto fl = [&]() -> Option<int>& { return l; };
+ auto fr = [&]() -> Option<int>&& { return std::move(r); };
+ auto fcv = [&]() -> const Option<int> { return v; };
+ auto fcl = [&]() -> const Option<int>& { return l; };
+ auto fcr = [&]() -> const Option<int>&& { return std::move(r); };
+
+ DIAG_PUSH();
+ DIAG_I(useless_cast);
+
+#define CHECK(v, t) \
+ { \
+ DECLTYPE_AUTO out = TRY_UNWRAP(v, abort() ); \
+ DECLTYPE_AUTO cmp = static_cast<t>(x); \
+ static_assert(std::is_same<decltype(out), decltype(cmp)>::value, #v); \
+ }
+
+ CHECK(v, int&);
+ CHECK(cv, const int&);
+ CHECK(l, int&);
+ CHECK(cl, const int&);
+ CHECK(r, int&);
+ CHECK(cr, const int&);
+ // repeat the same forcing expressions, since that matters with decltype
+ CHECK((v), int&);
+ CHECK((cv), const int&);
+ CHECK((l), int&);
+ CHECK((cl), const int&);
+ CHECK((r), int&);
+ CHECK((cr), const int&);
+
+ CHECK(fv(), int);
+ CHECK(fcv(), int);
+ CHECK(fl(), int&);
+ CHECK(fcl(), const int&);
+ CHECK(fr(), int&&);
+ CHECK(fcr(), const int&&);
+
+ DIAG_POP();
+#undef CHECK
+
+ v = None; TRY_UNWRAP(v, v = Some(1));
+ v = None; TRY_UNWRAP(l, v = Some(1));
+ v = None; TRY_UNWRAP(cl, v = Some(1));
+ v = None; TRY_UNWRAP(r, v = Some(1));
+ v = None; TRY_UNWRAP(cr, v = Some(1));
+
+ v = None; TRY_UNWRAP(fl(), v = Some(1));
+ v = None; TRY_UNWRAP(fcl(), v = Some(1));
+ v = None; TRY_UNWRAP(fr(), v = Some(1));
+ v = None; TRY_UNWRAP(fcr(), v = Some(1));
+
+ v = None;
+ OMATCH_BEGIN (v)
+ {
+ OMATCH_CASE_SOME (o)
+ {
+ EXPECT_NE(o, o);
+ }
+ OMATCH_CASE_NONE ()
+ {
+ SUCCEED();
+ }
+ }
+ OMATCH_END ();
+
+ v = Some(1);
+ OMATCH_BEGIN (v)
+ {
+ OMATCH_CASE_SOME (o)
+ {
+ EXPECT_EQ(o, 1);
+ }
+ OMATCH_CASE_NONE ()
+ {
+ FAIL();
+ }
+ }
+ OMATCH_END ();
+}
+
+TEST(Option, flatten)
+{
+ using option::Option;
+ using option::Some;
+ using option::None;
+
+ struct Foo
+ {
+ int x;
+ };
+ auto f1 = Some(Foo{42});
+ auto f2 = Some(f1);
+ auto f3 = Some(f2);
+ EXPECT_EQ(flatten(f1).copy_or(Foo{404}).x, 42);
+ EXPECT_EQ(flatten(f2).copy_or(Foo{404}).x, 42);
+ EXPECT_EQ(flatten(f3).copy_or(Foo{404}).x, 42);
+
+ decltype(f1) n1 = None;
+ decltype(f2) n2a = None;
+ decltype(f2) n2b = Some(n1);
+ decltype(f3) n3a = None;
+ decltype(f3) n3b = Some(n2a);
+ decltype(f3) n3c = Some(n2b);
+ EXPECT_EQ(flatten(n1).copy_or(Foo{404}).x, 404);
+ EXPECT_EQ(flatten(n2a).copy_or(Foo{404}).x, 404);
+ EXPECT_EQ(flatten(n2b).copy_or(Foo{404}).x, 404);
+ EXPECT_EQ(flatten(n3a).copy_or(Foo{404}).x, 404);
+ EXPECT_EQ(flatten(n3b).copy_or(Foo{404}).x, 404);
+ EXPECT_EQ(flatten(n3c).copy_or(Foo{404}).x, 404);
+}
+
+#define EQ(a, b) ({ EXPECT_TRUE(a == b); EXPECT_FALSE(a != b); EXPECT_FALSE(a < b); EXPECT_TRUE(a <= b); EXPECT_FALSE(a > b); EXPECT_TRUE(a >= b); })
+#define LT(a, b) ({ EXPECT_FALSE(a == b); EXPECT_TRUE(a != b); EXPECT_TRUE(a < b); EXPECT_TRUE(a <= b); EXPECT_FALSE(a > b); EXPECT_FALSE(a >= b); })
+#define GT(a, b) ({ EXPECT_FALSE(a == b); EXPECT_TRUE(a != b); EXPECT_FALSE(a < b); EXPECT_FALSE(a <= b); EXPECT_TRUE(a > b); EXPECT_TRUE(a >= b); })
+
+TEST(Option, cmp)
+{
+ using option::Option;
+ using option::Some;
+ using option::None;
+
+ Option<int> none = None;
+
+ EQ(none, none);
+ EQ(none, None);
+ LT(none, Some(-1));
+ LT(none, Some(0));
+ LT(none, Some(1));
+ EQ((None), none);
+ // EQ((None), None); // actually a function template
+ LT((None), Some(-1));
+ LT((None), Some(0));
+ LT((None), Some(1));
+ GT(Some(-1), none);
+ GT(Some(-1), None);
+ EQ(Some(-1), Some(-1));
+ LT(Some(-1), Some(0));
+ LT(Some(-1), Some(1));
+ GT(Some(0), none);
+ GT(Some(0), None);
+ GT(Some(0), Some(-1));
+ EQ(Some(0), Some(0));
+ LT(Some(0), Some(1));
+ GT(Some(1), none);
+ GT(Some(1), None);
+ GT(Some(1), Some(-1));
+ GT(Some(1), Some(0));
+ EQ(Some(1), Some(1));
+}
+
+} // namespace tmwa
diff --git a/src/compat/rawmem.hpp b/src/compat/rawmem.hpp
index c271a56..66af204 100644
--- a/src/compat/rawmem.hpp
+++ b/src/compat/rawmem.hpp
@@ -22,6 +22,8 @@
#include <cstdint>
#include <cstring>
+#include <type_traits>
+
#include "fwd.hpp"
@@ -49,4 +51,23 @@ void really_memset0(uint8_t *dest, size_t n)
{
memset(dest, '\0', n);
}
+
+template<class T>
+struct is_trivially_copyable
+: std::integral_constant<bool,
+ // come back when GCC actually implements the public traits properly
+ __has_trivial_copy(T)
+ && __has_trivial_assign(T)
+ && __has_trivial_destructor(T)>
+{};
+
+template<class T>
+void really_memzero_this(T *v)
+{
+ static_assert(is_trivially_copyable<T>::value, "only for mostly-pod types");
+ static_assert(std::is_class<T>::value || std::is_union<T>::value, "Only for user-defined structures (for now)");
+ memset(v, '\0', sizeof(*v));
+}
+template<class T, size_t n>
+void really_memzero_this(T (&)[n]) = delete;
} // namespace tmwa
diff --git a/src/compat/result.hpp b/src/compat/result.hpp
new file mode 100644
index 0000000..6adc552
--- /dev/null
+++ b/src/compat/result.hpp
@@ -0,0 +1,94 @@
+#pragma once
+// result.hpp - A possibly failed return value
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+#include "../strings/rstring.hpp"
+
+#include "option.hpp"
+
+namespace tmwa
+{
+ namespace result
+ {
+ enum ResultMagicFlag { magic_flag };
+
+ template<class T>
+ class Result
+ {
+ Option<T> success;
+ RString failure;
+ public:
+ Result(ResultMagicFlag, T v)
+ : success(Some(std::move(v))), failure()
+ {}
+ Result(ResultMagicFlag, RString msg)
+ : success(None), failure(std::move(msg))
+ {}
+
+ bool is_ok() { return success.is_some(); }
+ bool is_err() { return !is_ok(); }
+
+ Option<T>& get_success() { return success; }
+ RString& get_failure() { return failure; }
+ };
+
+ template<class T>
+ Result<T> Ok(T v)
+ {
+ return Result<T>(magic_flag, std::move(v));
+ }
+
+ struct Err
+ {
+ RString message;
+ Err(RString m) : message(std::move(m)) {}
+
+ template<class T>
+ operator Result<T>()
+ {
+ 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;
+ using result::Err;
+
+#define TRY(r) ({ auto _res = r; TRY_UNWRAP(_res.get_success(), return ::tmwa::Err(_res.get_failure())); })
+ // TODO the existence of this as a separate macro is a bug.
+#define TRY_MOVE(r) ({ auto _res = r; TRY_UNWRAP(std::move(_res.get_success()), return ::tmwa::Err(_res.get_failure())); })
+} // namespace tmwa
diff --git a/src/compat/result_test.cpp b/src/compat/result_test.cpp
new file mode 100644
index 0000000..0fcc181
--- /dev/null
+++ b/src/compat/result_test.cpp
@@ -0,0 +1,79 @@
+#include "result.hpp"
+// result_test.cpp - Testsuite for possibly failing return values
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 <gtest/gtest.h>
+
+#include "../strings/literal.hpp"
+
+//#include "../poison.hpp"
+
+
+namespace tmwa
+{
+TEST(Result, inspect)
+{
+ struct Foo
+ {
+ int val;
+
+ Foo(int v) : val(v) {}
+ Foo(Foo&&) = default;
+ Foo(const Foo&) = delete;
+ Foo& operator = (Foo&&) = default;
+ Foo& operator = (const Foo&) = delete;
+
+ bool operator == (const Foo& other) const
+ {
+ return this->val == other.val;
+ }
+ };
+
+ Result<Foo> foo = Ok(Foo(1));
+ EXPECT_TRUE(foo.is_ok());
+ EXPECT_FALSE(foo.is_err());
+ EXPECT_EQ(foo.get_success(), Some(Foo(1)));
+ EXPECT_EQ(foo.get_failure(), ""_s);
+ foo = Err("oops"_s);
+ EXPECT_FALSE(foo.is_ok());
+ EXPECT_TRUE(foo.is_err());
+ EXPECT_EQ(foo.get_success(), None<Foo>());
+ EXPECT_EQ(foo.get_failure(), "oops"_s);
+}
+
+static
+Result<int> try_you(bool b)
+{
+ return b ? Ok(0) : Err("die"_s);
+}
+
+static
+Result<int> try_me(bool b)
+{
+ return Ok(TRY(try_you(b)) + 1);
+}
+
+TEST(Result, try)
+{
+ Result<int> t = try_me(true);
+ EXPECT_EQ(t.get_success(), Some(1));
+ Result<int> f = try_me(false);
+ EXPECT_EQ(f.get_failure(), "die"_s);
+}
+} // namespace tmwa
diff --git a/src/compat/time_t.cpp b/src/compat/time_t.cpp
deleted file mode 100644
index ee0bbde..0000000
--- a/src/compat/time_t.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "time_t.hpp"
-// time_t.cpp - time_t with a reliable representation
-//
-// Copyright © 2013-2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/compat/time_t.hpp b/src/compat/time_t.hpp
index 9e7cf25..e9c97c4 100644
--- a/src/compat/time_t.hpp
+++ b/src/compat/time_t.hpp
@@ -20,10 +20,90 @@
#include "fwd.hpp"
-// TODO fix this ordering violation by promoting TimeT here
-#include "../mmo/utils.hpp"
+#include <ctime>
+
+#include "../ints/little.hpp"
+
+#include "operators.hpp"
namespace tmwa
{
+// Exists in place of time_t, to give it a predictable printf-format.
+// (on x86 and amd64, time_t == long, but not on x32)
+static_assert(sizeof(long long) >= sizeof(time_t), "long long >= time_t");
+struct TimeT : Comparable
+{
+ long long value;
+
+ // conversion
+ TimeT(time_t t=0) : value(t) {}
+ TimeT(struct tm t) : value(timegm(&t)) {}
+ operator time_t() const { return value; }
+ operator struct tm() const { time_t v = value; return *gmtime(&v); }
+
+ explicit operator bool() const { return value; }
+ bool operator !() const { return !value; }
+
+ // prevent surprises
+ template<class T>
+ TimeT(T) = delete;
+ template<class T>
+ operator T() const = delete;
+
+ static
+ TimeT now()
+ {
+ // poisoned, but this is still in header-land
+ return time(nullptr);
+ }
+
+ bool error() const
+ {
+ return value == -1;
+ }
+ bool okay() const
+ {
+ return !error();
+ }
+};
+
+inline
+long long convert_for_printf(TimeT t)
+{
+ return t.value;
+}
+
+// 2038 problem
+inline __attribute__((warn_unused_result))
+bool native_to_network(Little32 *net, TimeT nat)
+{
+ time_t tmp = nat;
+ return native_to_network(net, static_cast<uint32_t>(tmp));
+}
+
+inline __attribute__((warn_unused_result))
+bool network_to_native(TimeT *nat, Little32 net)
+{
+ uint32_t tmp;
+ bool rv = network_to_native(&tmp, net);
+ *nat = static_cast<time_t>(tmp);
+ return rv;
+}
+
+inline __attribute__((warn_unused_result))
+bool native_to_network(Little64 *net, TimeT nat)
+{
+ time_t tmp = nat;
+ return native_to_network(net, static_cast<uint64_t>(tmp));
+}
+
+inline __attribute__((warn_unused_result))
+bool network_to_native(TimeT *nat, Little64 net)
+{
+ uint64_t tmp;
+ bool rv = network_to_native(&tmp, net);
+ *nat = static_cast<time_t>(tmp);
+ return rv;
+}
} // namespace tmwa
diff --git a/src/diagnostics.hpp b/src/diagnostics.hpp
index dab15b5..5eee323 100644
--- a/src/diagnostics.hpp
+++ b/src/diagnostics.hpp
@@ -600,7 +600,8 @@ namespace tmwa
/// Warn about maybe uninitialized automatic variables
#define DIAG_maybe_uninitialized "-Wmaybe-uninitialized"
-#if GCC >= 407
+// buggy in 4.7 with tmwa::Option<>
+#if GCC >= 408
# define HAS_DIAG_maybe_uninitialized 1
#else
# define HAS_DIAG_maybe_uninitialized 0
diff --git a/src/generic/array.hpp b/src/generic/array.hpp
index dccb91e..3575db6 100644
--- a/src/generic/array.hpp
+++ b/src/generic/array.hpp
@@ -26,6 +26,7 @@
#include "oops.hpp"
+// half the important stuff is now in fwd.hpp !!!
namespace tmwa
{
template<class I, I be, I en>
@@ -39,9 +40,6 @@ struct ExclusiveIndexing
constexpr static size_t alloc_size = index_to_offset(en) - index_to_offset(be);
};
-template<size_t n>
-using SimpleIndexing = ExclusiveIndexing<size_t, 0, n>;
-
template<class I, I lo, I hi>
struct InclusiveIndexing
{
@@ -53,7 +51,7 @@ struct InclusiveIndexing
constexpr static size_t alloc_size = index_to_offset(hi) - index_to_offset(lo) + 1;
};
-template<class E, E n=E::COUNT>
+template<class E, E n>
struct EnumIndexing : ExclusiveIndexing<E, static_cast<E>(0), n>
{
};
@@ -112,7 +110,4 @@ public:
return !(lhs == rhs);
}
};
-
-template<class T, size_t n>
-using Array = GenericArray<T, SimpleIndexing<n>>;
} // namespace tmwa
diff --git a/src/generic/db.cpp b/src/generic/db.cpp
deleted file mode 100644
index 458068c..0000000
--- a/src/generic/db.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "db.hpp"
-// db.cpp - convenience wrappers over std::map<K, V>
-//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/generic/db.hpp b/src/generic/db.hpp
index 90c4f92..04ead79 100644
--- a/src/generic/db.hpp
+++ b/src/generic/db.hpp
@@ -23,6 +23,8 @@
#include <map>
#include <memory>
+#include "../compat/borrow.hpp"
+
namespace tmwa
{
@@ -45,19 +47,19 @@ public:
const_iterator begin() const { return impl.begin(); }
const_iterator end() const { return impl.end(); }
- V *search(const K& k)
+ Option<Borrowed<V>> search(const K& k)
{
iterator it = impl.find(k);
if (it == impl.end())
- return nullptr;
- return &it->second;
+ return None;
+ return Some(borrow(it->second));
}
- const V *search(const K& k) const
+ Option<Borrowed<const V>> search(const K& k) const
{
const_iterator it = impl.find(k);
if (it == impl.end())
- return nullptr;
- return &it->second;
+ return None;
+ return Some(borrow(it->second));
}
void insert(const K& k, V v)
{
@@ -72,9 +74,9 @@ public:
return (void)&it->second;
}
- V *init(const K& k)
+ Borrowed<V> init(const K& k)
{
- return &impl[k];
+ return borrow(impl[k]);
}
void erase(const K& k)
{
@@ -112,8 +114,13 @@ public:
// const V& ? with a static default V?
V get(const K& k)
{
- V *vp = impl.search(k);
- return vp ? *vp : V();
+ Option<Borrowed<V>> vp = impl.search(k);
+ OMATCH_BEGIN_SOME (v, vp)
+ {
+ return *v;
+ }
+ OMATCH_END ();
+ return V();
}
void put(const K& k, V v)
{
@@ -153,10 +160,15 @@ public:
const_iterator end() const { return impl.end(); }
// const V& ? with a static default V?
- V *get(const K& k)
- {
- U *up = impl.search(k);
- return up ? up->get() : nullptr;
+ Option<Borrowed<V>> get(const K& k)
+ {
+ Option<Borrowed<U>> up = impl.search(k);
+ OMATCH_BEGIN_SOME (u, up)
+ {
+ return Some(borrow(*u->get()));
+ }
+ OMATCH_END ();
+ return None;
}
void put(const K& k, U v)
{
diff --git a/src/generic/dumb_ptr.cpp b/src/generic/dumb_ptr.cpp
deleted file mode 100644
index e690f7d..0000000
--- a/src/generic/dumb_ptr.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "dumb_ptr.hpp"
-// dumb_ptr.cpp - dummy file to make Make dependencies work
-//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/generic/dumb_ptr.hpp b/src/generic/dumb_ptr.hpp
index 72247d5..2ada93b 100644
--- a/src/generic/dumb_ptr.hpp
+++ b/src/generic/dumb_ptr.hpp
@@ -35,8 +35,11 @@ class dumb_ptr
friend class dumb_ptr;
T *impl;
public:
+ dumb_ptr() noexcept
+ : impl(nullptr)
+ {}
explicit
- dumb_ptr(T *p=nullptr) noexcept
+ dumb_ptr(T *p) noexcept
: impl(p)
{}
template<class U>
diff --git a/src/generic/enum.hpp b/src/generic/enum.hpp
index 81c9b12..d5d50ea 100644
--- a/src/generic/enum.hpp
+++ b/src/generic/enum.hpp
@@ -33,8 +33,7 @@
namespace tmwa
{
-template<class T, class E, E max>
-using earray = GenericArray<T, EnumIndexing<E, max>>;
+// part moved to fwd.hpp
template<class T, class E, E max>
class eptr
diff --git a/src/generic/fwd.hpp b/src/generic/fwd.hpp
index 3215903..31fb13a 100644
--- a/src/generic/fwd.hpp
+++ b/src/generic/fwd.hpp
@@ -20,10 +20,42 @@
#include "../sanity.hpp"
+#include "../strings/fwd.hpp" // rank 1
+#include "../compat/fwd.hpp" // rank 2
+// generic/fwd.hpp is rank 3
+
namespace tmwa
{
// meh, add more when I feel like it
template<class T>
class dumb_ptr;
+
+template<class K, class V>
+class Map;
+template<class K, class V>
+class DMap;
+template<class K, class V>
+class UPMap;
+
+class InternPool;
+
+// arrays are complicated
+template<class I, I be, I en>
+struct ExclusiveIndexing;
+template<size_t n>
+using SimpleIndexing = ExclusiveIndexing<size_t, 0, n>;
+template<class I, I lo, I hi>
+struct InclusiveIndexing;
+template<class E, E n=E::COUNT>
+struct EnumIndexing;
+template<class I, size_t limit>
+struct InventoryIndexing;
+template<class T, class I>
+struct GenericArray;
+template<class T, size_t n>
+using Array = GenericArray<T, SimpleIndexing<n>>;
+
+template<class T, class E, E max>
+using earray = GenericArray<T, EnumIndexing<E, max>>;
} // namespace tmwa
diff --git a/src/generic/matrix.cpp b/src/generic/matrix.cpp
deleted file mode 100644
index b14ab7d..0000000
--- a/src/generic/matrix.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "matrix.hpp"
-// matrix.cpp - A 2D array.
-//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/generic/md5.hpp b/src/generic/md5.hpp
index 50bc987..2f07789 100644
--- a/src/generic/md5.hpp
+++ b/src/generic/md5.hpp
@@ -24,7 +24,6 @@
#include <array>
-#include "../strings/fwd.hpp"
#include "../strings/vstring.hpp"
diff --git a/src/generic/operators.cpp b/src/generic/operators.cpp
deleted file mode 100644
index 614ae51..0000000
--- a/src/generic/operators.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "operators.hpp"
-// operators.cpp - ADL helper for value wrappers.
-//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/generic/random.hpp b/src/generic/random.hpp
index 5d67236..897ad43 100644
--- a/src/generic/random.hpp
+++ b/src/generic/random.hpp
@@ -18,10 +18,10 @@
// 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 "fwd.hpp"
-
#include "random.t.hpp"
+#include "fwd.hpp"
+
#include <random>
diff --git a/src/generic/random2.hpp b/src/generic/random2.hpp
index 23d165c..3d481f4 100644
--- a/src/generic/random2.hpp
+++ b/src/generic/random2.hpp
@@ -18,10 +18,10 @@
// 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 "fwd.hpp"
-
#include "random.hpp"
+#include "fwd.hpp"
+
#include <algorithm>
#include "../compat/iter.hpp"
diff --git a/src/mmo/core.cpp b/src/high/core.cpp
index ff12f17..8a4d7ca 100644
--- a/src/mmo/core.cpp
+++ b/src/high/core.cpp
@@ -23,6 +23,7 @@
#include <sys/wait.h>
#include <alloca.h>
+#include <unistd.h>
#include <csignal>
#include <cstdlib>
@@ -106,8 +107,21 @@ void sig_proc(int)
*/
} // namespace tmwa
+static
+void check_caps()
+{
+ if (geteuid() == 0)
+ {
+ puts("Please don't run as root!");
+ _exit(1);
+ }
+}
+
int tmwa_main(int argc, char **argv)
{
+ // run before anything else (except global constructors)
+ check_caps();
+
using namespace tmwa;
check_paths();
@@ -153,8 +167,8 @@ int tmwa_main(int argc, char **argv)
// may wait too long in sendrecv
tick_t now = milli_clock::now();
interval_t next = do_timer(now);
- do_sendrecv(next);
- do_parsepacket();
+ runflag &= do_sendrecv(next);
+ runflag &= do_parsepacket();
}
return 0;
diff --git a/src/mmo/core.hpp b/src/high/core.hpp
index 259dd90..0ded246 100644
--- a/src/mmo/core.hpp
+++ b/src/high/core.hpp
@@ -24,8 +24,6 @@
#include "../range/slice.hpp"
-#include "../strings/fwd.hpp"
-
namespace tmwa
{
diff --git a/src/mmo/extract.cpp b/src/high/extract_mmo.cpp
index a480984..ab4290d 100644
--- a/src/mmo/extract.cpp
+++ b/src/high/extract_mmo.cpp
@@ -1,5 +1,5 @@
-#include "extract.hpp"
-// extract.cpp - a simple, hierarchical, tokenizer
+#include "extract_mmo.hpp"
+// extract_mmo.cpp - a simple, hierarchical, tokenizer
//
// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
//
@@ -20,37 +20,25 @@
#include <algorithm>
-#include "../strings/astring.hpp"
-#include "../strings/xstring.hpp"
-#include "../strings/vstring.hpp"
+#include "../io/extract.hpp"
+
+#include "../mmo/extract_enums.hpp"
-#include "extract_enums.hpp"
#include "mmo.hpp"
#include "../poison.hpp"
+// TODO also pass an io::LineSpan around.
namespace tmwa
{
-bool extract(XString str, XString *rv)
-{
- *rv = str;
- return true;
-}
-
-bool extract(XString str, AString *rv)
-{
- *rv = str;
- return true;
-}
-
-bool extract(XString str, GlobalReg *var)
+bool impl_extract(XString str, GlobalReg *var)
{
return extract(str,
record<','>(&var->str, &var->value));
}
-bool extract(XString str, Item *it)
+bool impl_extract(XString str, Item *it)
{
XString ignored;
XString corruption_hack_amount;
@@ -78,7 +66,7 @@ bool extract(XString str, Item *it)
return rv;
}
-bool extract(XString str, MapName *m)
+bool impl_extract(XString str, MapName *m)
{
XString::iterator it = std::find(str.begin(), str.end(), '.');
str = str.xislice_h(it);
@@ -88,7 +76,7 @@ bool extract(XString str, MapName *m)
return rv;
}
-bool extract(XString str, CharName *out)
+bool impl_extract(XString str, CharName *out)
{
VString<23> tmp;
if (extract(str, &tmp))
@@ -98,4 +86,15 @@ bool extract(XString str, CharName *out)
}
return false;
}
+
+bool impl_extract(XString str, NpcEvent *ev)
+{
+ XString mid;
+ return extract(str, record<':'>(&ev->npc, &mid, &ev->label)) && !mid;
+}
+
+bool impl_extract(XString str, Point *p)
+{
+ return extract(str, record<','>(&p->map_, &p->x, &p->y));
+}
} // namespace tmwa
diff --git a/src/compat/rawmem.cpp b/src/high/extract_mmo.hpp
index d322437..f374658 100644
--- a/src/compat/rawmem.cpp
+++ b/src/high/extract_mmo.hpp
@@ -1,7 +1,7 @@
-#include "rawmem.hpp"
-// rawmem.cpp - Ignore poisoning and really frob this memory unsafely.
+#pragma once
+// extract_mmo.hpp - a simple, hierarchical, tokenizer
//
-// Copyright © 2013-2014 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2012-2013 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -18,9 +18,16 @@
// 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 "../poison.hpp"
+#include "fwd.hpp"
namespace tmwa
{
+bool impl_extract(XString str, GlobalReg *var);
+bool impl_extract(XString str, Item *it);
+bool impl_extract(XString str, MapName *m);
+bool impl_extract(XString str, CharName *out);
+
+bool impl_extract(XString str, NpcEvent *ev);
+bool impl_extract(XString str, Point *p);
} // namespace tmwa
diff --git a/src/high/fwd.hpp b/src/high/fwd.hpp
new file mode 100644
index 0000000..96875d3
--- /dev/null
+++ b/src/high/fwd.hpp
@@ -0,0 +1,40 @@
+#pragma once
+// high/fwd.hpp - list of type names for mmo lib
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "../sanity.hpp"
+
+#include "../range/fwd.hpp" // rank 1
+#include "../strings/fwd.hpp" // rank 1
+#include "../compat/fwd.hpp" // rank 2
+#include "../generic/fwd.hpp" // rank 3
+#include "../io/fwd.hpp" // rank 4
+#include "../net/fwd.hpp" // rank 5
+#include "../mmo/fwd.hpp" // rank 6
+#include "../proto2/fwd.hpp" // rank 8
+// high/fwd.hpp is rank 9
+
+
+namespace tmwa
+{
+class CharPair;
+class PartyPair;
+struct GM_Account;
+// meh, add more when I feel like it
+} // namespace tmwa
diff --git a/src/mmo/md5more.cpp b/src/high/md5more.cpp
index 4e5d2da..05149ac 100644
--- a/src/mmo/md5more.cpp
+++ b/src/high/md5more.cpp
@@ -31,7 +31,7 @@
#include "../net/ip.hpp"
-#include "../mmo/mmo.hpp"
+#include "mmo.hpp"
#include "../poison.hpp"
diff --git a/src/mmo/md5more.hpp b/src/high/md5more.hpp
index 7d50713..33ebe24 100644
--- a/src/mmo/md5more.hpp
+++ b/src/high/md5more.hpp
@@ -24,10 +24,6 @@
#include "../generic/md5.hpp"
-#include "../io/fwd.hpp"
-
-#include "../net/fwd.hpp"
-
namespace tmwa
{
diff --git a/src/mmo/mmo.hpp b/src/high/mmo.hpp
index cfa278d..b5bcac8 100644
--- a/src/mmo/mmo.hpp
+++ b/src/high/mmo.hpp
@@ -22,9 +22,13 @@
#include "fwd.hpp"
+#include "../compat/borrow.hpp"
#include "../compat/memory.hpp"
-#include "../proto2/types.hpp"
+#include "../proto2/net-CharData.hpp"
+#include "../proto2/net-CharKey.hpp"
+#include "../proto2/net-PartyMost.hpp"
+#include "../proto2/net-SkillValue.hpp"
namespace tmwa
@@ -58,12 +62,10 @@ struct GM_Account
struct PartyPair
{
- PartyId party_id = {};
- PartyMost *party_most = {};
+ PartyId party_id;
+ Borrowed<PartyMost> party_most;
- explicit
- operator bool() const { return party_most; }
- bool operator !() const { return !party_most; }
- PartyMost *operator->() const { return party_most; }
+ PartyMost& operator *() const { return *party_most; }
+ Borrowed<PartyMost> operator->() const { return party_most; }
};
} // namespace tmwa
diff --git a/src/mmo/utils.cpp b/src/high/utils.cpp
index f8aff2e..9b89c31 100644
--- a/src/mmo/utils.cpp
+++ b/src/high/utils.cpp
@@ -28,9 +28,7 @@
#include "../strings/xstring.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/write.hpp"
-
-#include "extract.hpp"
+#include "../io/extract.hpp"
#include "../poison.hpp"
@@ -85,39 +83,4 @@ int config_switch(ZString str)
FPRINTF(stderr, "Fatal: bad option value %s"_fmt, str);
abort();
}
-
-static_assert(sizeof(timestamp_seconds_buffer) == 20, "seconds buffer");
-static_assert(sizeof(timestamp_milliseconds_buffer) == 24, "millis buffer");
-
-void stamp_time(timestamp_seconds_buffer& out, const TimeT *t)
-{
- struct tm when = t ? *t : TimeT::now();
- char buf[20];
- strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &when);
- out = stringish<timestamp_seconds_buffer>(VString<19>(strings::really_construct_from_a_pointer, buf));
-}
-void stamp_time(timestamp_milliseconds_buffer& out)
-{
- struct timeval tv;
- gettimeofday(&tv, nullptr);
- struct tm when = TimeT(tv.tv_sec);
- char buf[24];
- strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &when);
- sprintf(buf + 19, ".%03d", static_cast<int>(tv.tv_usec / 1000));
- out = stringish<timestamp_milliseconds_buffer>(VString<23>(strings::really_construct_from_a_pointer, buf));
-}
-
-void log_with_timestamp(io::WriteFile& out, XString line)
-{
- if (!line)
- {
- out.put('\n');
- return;
- }
- timestamp_milliseconds_buffer tmpstr;
- stamp_time(tmpstr);
- out.really_put(tmpstr.data(), tmpstr.size());
- out.really_put(": ", 2);
- out.put_line(line);
-}
} // namespace tmwa
diff --git a/src/compat/cast.cpp b/src/high/utils.hpp
index 482529d..c815e03 100644
--- a/src/compat/cast.cpp
+++ b/src/high/utils.hpp
@@ -1,6 +1,8 @@
-#include "cast.hpp"
-// cast.cpp - Change the type of a variable.
+#pragma once
+// utils.hpp - Useful stuff that hasn't been categorized.
//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
@@ -18,9 +20,10 @@
// 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 "../poison.hpp"
-
+#include "fwd.hpp"
namespace tmwa
{
+bool e_mail_check(XString email);
+int config_switch(ZString str);
} // namespace tmwa
diff --git a/src/ints/fwd.hpp b/src/ints/fwd.hpp
index a08e546..536eba1 100644
--- a/src/ints/fwd.hpp
+++ b/src/ints/fwd.hpp
@@ -20,6 +20,8 @@
#include "../sanity.hpp"
+// ints/fwd.hpp is rank 1
+
namespace tmwa
{
diff --git a/src/ints/little.cpp b/src/ints/little.cpp
deleted file mode 100644
index 0ae5bf7..0000000
--- a/src/ints/little.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "little.hpp"
-// little.cpp - integers of known endianness
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/ints/udl.cpp b/src/ints/udl.cpp
deleted file mode 100644
index 3988903..0000000
--- a/src/ints/udl.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "udl.hpp"
-// udl.cpp - user-defined literals for integers.
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/ints/wrap.cpp b/src/ints/wrap.cpp
deleted file mode 100644
index 84d4b33..0000000
--- a/src/ints/wrap.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "wrap.hpp"
-// wrap.cpp - basic integer wrapper classes
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/ints/wrap.py b/src/ints/wrap.py
index c8a8c6d..d5a6e99 100644
--- a/src/ints/wrap.py
+++ b/src/ints/wrap.py
@@ -9,5 +9,10 @@ class Wrapped(object):
def to_string(self):
return self._value['_value']
+ test_extra = '''
+ void do_breakpoint();
+ void do_breakpoint() {}
+ '''
+
# tests are in src/mmo/ids.py instead
tests = []
diff --git a/src/io/cxxstdio.cpp b/src/io/cxxstdio.cpp
deleted file mode 100644
index ca4e880..0000000
--- a/src/io/cxxstdio.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "cxxstdio.hpp"
-// cxxstdio.cpp - pass C++ types through printf
-//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/io/cxxstdio_enums.cpp b/src/io/cxxstdio_enums.cpp
deleted file mode 100644
index 216da1d..0000000
--- a/src/io/cxxstdio_enums.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "cxxstdio_enums.hpp"
-// cxxstdio_enums.cpp - Opt-in integer formatting support for enums.
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/io/dir.hpp b/src/io/dir.hpp
index 071f309..f6fedbf 100644
--- a/src/io/dir.hpp
+++ b/src/io/dir.hpp
@@ -20,8 +20,6 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
#include "fd.hpp"
diff --git a/src/io/extract.cpp b/src/io/extract.cpp
new file mode 100644
index 0000000..fce4dab
--- /dev/null
+++ b/src/io/extract.cpp
@@ -0,0 +1,220 @@
+#include "extract.hpp"
+// extract.cpp - a simple, hierarchical, tokenizer
+//
+// Copyright © 2013-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 <algorithm>
+
+#include "../strings/astring.hpp"
+#include "../strings/xstring.hpp"
+#include "../strings/vstring.hpp"
+
+#include "../poison.hpp"
+
+
+// TODO also pass an io::LineSpan around.
+namespace tmwa
+{
+bool impl_extract(XString str, XString *rv)
+{
+ *rv = str;
+ return true;
+}
+
+bool impl_extract(XString str, RString *rv)
+{
+ *rv = str;
+ return true;
+}
+
+bool impl_extract(XString str, AString *rv)
+{
+ *rv = str;
+ return true;
+}
+
+bool impl_extract(XString str, std::chrono::nanoseconds *ns)
+{
+ std::chrono::nanoseconds::rep rep;
+ if (extract(str, &rep))
+ {
+ *ns = std::chrono::nanoseconds(rep);
+ return true;
+ }
+ if (str.endswith("ns"_s))
+ {
+ if (extract(str.xrslice_h("ns"_s.size()), &rep))
+ {
+ *ns = std::chrono::nanoseconds(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::microseconds bigger;
+ if (extract(str, &bigger))
+ {
+ *ns = bigger;
+ return *ns == bigger;
+ }
+ return false;
+}
+bool impl_extract(XString str, std::chrono::microseconds *us)
+{
+ std::chrono::microseconds::rep rep;
+ if (extract(str, &rep))
+ {
+ *us = std::chrono::microseconds(rep);
+ return true;
+ }
+ if (str.endswith("us"_s))
+ {
+ if (extract(str.xrslice_h("us"_s.size()), &rep))
+ {
+ *us = std::chrono::microseconds(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::milliseconds bigger;
+ if (extract(str, &bigger))
+ {
+ *us = bigger;
+ return *us == bigger;
+ }
+ return false;
+}
+bool impl_extract(XString str, std::chrono::milliseconds *ms)
+{
+ std::chrono::milliseconds::rep rep;
+ if (extract(str, &rep))
+ {
+ *ms = std::chrono::milliseconds(rep);
+ return true;
+ }
+ if (str.endswith("ms"_s))
+ {
+ if (extract(str.xrslice_h("ms"_s.size()), &rep))
+ {
+ *ms = std::chrono::milliseconds(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::seconds bigger;
+ if (extract(str, &bigger))
+ {
+ *ms = bigger;
+ return *ms == bigger;
+ }
+ return false;
+}
+bool impl_extract(XString str, std::chrono::seconds *s)
+{
+ std::chrono::seconds::rep rep;
+ if (extract(str, &rep))
+ {
+ *s = std::chrono::seconds(rep);
+ return true;
+ }
+ if (str.endswith("s"_s))
+ {
+ if (extract(str.xrslice_h("s"_s.size()), &rep))
+ {
+ *s = std::chrono::seconds(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::minutes bigger;
+ if (extract(str, &bigger))
+ {
+ *s = bigger;
+ return *s == bigger;
+ }
+ return false;
+}
+bool impl_extract(XString str, std::chrono::minutes *min)
+{
+ std::chrono::minutes::rep rep;
+ if (extract(str, &rep))
+ {
+ *min = std::chrono::minutes(rep);
+ return true;
+ }
+ if (str.endswith("min"_s))
+ {
+ if (extract(str.xrslice_h("min"_s.size()), &rep))
+ {
+ *min = std::chrono::minutes(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::hours bigger;
+ if (extract(str, &bigger))
+ {
+ *min = bigger;
+ return *min == bigger;
+ }
+ return false;
+}
+bool impl_extract(XString str, std::chrono::hours *h)
+{
+ std::chrono::hours::rep rep;
+ if (extract(str, &rep))
+ {
+ *h = std::chrono::hours(rep);
+ return true;
+ }
+ if (str.endswith("h"_s))
+ {
+ if (extract(str.xrslice_h("h"_s.size()), &rep))
+ {
+ *h = std::chrono::hours(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::duration<int, std::ratio<60*60*24>> bigger;
+ if (extract(str, &bigger))
+ {
+ *h = bigger;
+ return *h == bigger;
+ }
+ return false;
+}
+bool impl_extract(XString str, std::chrono::duration<int, std::ratio<60*60*24>> *d)
+{
+ std::chrono::duration<int, std::ratio<60*60*24>>::rep rep;
+ if (extract(str, &rep))
+ {
+ *d = std::chrono::duration<int, std::ratio<60*60*24>>(rep);
+ return true;
+ }
+ if (str.endswith("d"_s))
+ {
+ if (extract(str.xrslice_h("d"_s.size()), &rep))
+ {
+ *d = std::chrono::duration<int, std::ratio<60*60*24>>(rep);
+ return true;
+ }
+ return false;
+ }
+ return false;
+}
+} // namespace tmwa
diff --git a/src/mmo/extract.hpp b/src/io/extract.hpp
index ed2eb78..897f50e 100644
--- a/src/mmo/extract.hpp
+++ b/src/io/extract.hpp
@@ -24,24 +24,25 @@
#include <cstdlib>
#include <algorithm>
+#include <chrono>
#include <vector>
#include "../ints/wrap.hpp"
#include "../strings/xstring.hpp"
-#include "../generic/enum.hpp"
+#include "../compat/time_t.hpp"
-#include "utils.hpp"
+#include "../generic/enum.hpp"
namespace tmwa
{
template<class T>
-bool do_extract(XString str, T t);
+bool extract(XString str, T t);
template<class T, typename=typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value && !std::is_same<T, bool>::value>::type>
-bool extract(XString str, T *iv)
+bool impl_extract(XString str, T *iv)
{
if (!str || str.size() > 20)
return false;
@@ -74,7 +75,7 @@ bool extract(XString str, T *iv)
}
inline
-bool extract(XString str, TimeT *tv)
+bool impl_extract(XString str, TimeT *tv)
{
return extract(str, &tv->value);
}
@@ -92,12 +93,12 @@ bool extract_as_int(XString str, T *iv)
return true;
}
-bool extract(XString str, XString *rv);
-
-bool extract(XString str, AString *rv);
+bool impl_extract(XString str, XString *rv);
+bool impl_extract(XString str, RString *rv);
+bool impl_extract(XString str, AString *rv);
template<uint8_t N>
-bool extract(XString str, VString<N> *out)
+bool impl_extract(XString str, VString<N> *out)
{
if (str.size() > N)
return false;
@@ -106,7 +107,7 @@ bool extract(XString str, VString<N> *out)
}
inline
-bool extract(XString str, LString exact)
+bool impl_extract(XString str, LString exact)
{
return str == exact;
}
@@ -125,7 +126,7 @@ LStripper<T> lstripping(T v)
}
template<class T>
-bool extract(XString str, LStripper<T> out)
+bool impl_extract(XString str, LStripper<T> out)
{
return extract(str.lstrip(), out.impl);
}
@@ -162,13 +163,13 @@ Record<split, n, T...> record(T... t)
}
template<char split, int n>
-bool extract(XString str, Record<split, n>)
+bool impl_extract(XString str, Record<split, n>)
{
return !str;
}
template<char split, int n, class F, class... R>
-bool extract(XString str, Record<split, n, F, R...> rec)
+bool impl_extract(XString str, Record<split, n, F, R...> rec)
{
XString::iterator s = std::find(str.begin(), str.end(), split);
XString::iterator s2 = s;
@@ -197,7 +198,7 @@ VRecord<split, T> vrec(std::vector<T> *arr)
}
template<char split, class T>
-bool extract(XString str, VRecord<split, T> rec)
+bool impl_extract(XString str, VRecord<split, T> rec)
{
if (!str)
return true;
@@ -209,23 +210,25 @@ bool extract(XString str, VRecord<split, T> rec)
&& extract(str.xislice_t(s + 1), rec);
}
-bool extract(XString str, GlobalReg *var);
-
-bool extract(XString str, Item *it);
-
-bool extract(XString str, MapName *m);
-
-bool extract(XString str, CharName *out);
-
-template<class T>
-bool do_extract(XString str, T t)
+template<class R>
+bool impl_extract(XString str, Wrapped<R> *w)
{
- return extract(str, t);
+ return extract(str, &w->_value);
}
-template<class R>
-bool extract(XString str, Wrapped<R> *w)
+bool impl_extract(XString str, std::chrono::nanoseconds *ns);
+bool impl_extract(XString str, std::chrono::microseconds *us);
+bool impl_extract(XString str, std::chrono::milliseconds *ms);
+bool impl_extract(XString str, std::chrono::seconds *s);
+bool impl_extract(XString str, std::chrono::minutes *min);
+bool impl_extract(XString str, std::chrono::hours *h);
+bool impl_extract(XString str, std::chrono::duration<int, std::ratio<60*60*24>> *d);
+
+// this must come after all non-`namespace tmwa` `impl_extract`s.
+// In particular, the ones for `*int*` and `std::chrono::*`
+template<class T>
+bool extract(XString str, T t)
{
- return extract(str, &w->_value);
+ return impl_extract(str, t);
}
} // namespace tmwa
diff --git a/src/mmo/extract_test.cpp b/src/io/extract_test.cpp
index e6dc7b2..ee4cb08 100644
--- a/src/mmo/extract_test.cpp
+++ b/src/io/extract_test.cpp
@@ -22,7 +22,11 @@
#include "../strings/xstring.hpp"
-#include "mmo.hpp"
+#include "../net/timer.t.hpp"
+
+#include "../mmo/strs.hpp"
+
+#include "../high/extract_mmo.hpp"
#include "../poison.hpp"
@@ -357,4 +361,92 @@ TEST(extract, mapname)
EXPECT_TRUE(extract("abcdefghijklmno.gat"_s, &map));
EXPECT_EQ(map, "abcdefghijklmno"_s);
}
+
+TEST(extract, chrono)
+{
+ std::chrono::nanoseconds ns;
+ std::chrono::microseconds us;
+ std::chrono::milliseconds ms;
+ std::chrono::seconds s;
+ std::chrono::minutes min;
+ std::chrono::hours h;
+ std::chrono::duration<int, std::ratio<60*60*24>> d;
+
+ EXPECT_TRUE(extract("1"_s, &ns));
+ EXPECT_EQ(ns, 1_ns);
+ EXPECT_TRUE(extract("3ns"_s, &ns));
+ EXPECT_EQ(ns, 3_ns);
+ EXPECT_TRUE(extract("4us"_s, &ns));
+ EXPECT_EQ(ns, 4_us);
+ EXPECT_TRUE(extract("5ms"_s, &ns));
+ EXPECT_EQ(ns, 5_ms);
+ EXPECT_TRUE(extract("6s"_s, &ns));
+ EXPECT_EQ(ns, 6_s);
+ EXPECT_TRUE(extract("7min"_s, &ns));
+ EXPECT_EQ(ns, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &ns));
+ EXPECT_EQ(ns, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &ns));
+ EXPECT_EQ(ns, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &us));
+ EXPECT_EQ(us, 1_us);
+ EXPECT_TRUE(extract("4us"_s, &us));
+ EXPECT_EQ(us, 4_us);
+ EXPECT_TRUE(extract("5ms"_s, &us));
+ EXPECT_EQ(us, 5_ms);
+ EXPECT_TRUE(extract("6s"_s, &us));
+ EXPECT_EQ(us, 6_s);
+ EXPECT_TRUE(extract("7min"_s, &us));
+ EXPECT_EQ(us, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &us));
+ EXPECT_EQ(us, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &us));
+ EXPECT_EQ(us, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &ms));
+ EXPECT_EQ(ms, 1_ms);
+ EXPECT_TRUE(extract("5ms"_s, &ms));
+ EXPECT_EQ(ms, 5_ms);
+ EXPECT_TRUE(extract("6s"_s, &ms));
+ EXPECT_EQ(ms, 6_s);
+ EXPECT_TRUE(extract("7min"_s, &ms));
+ EXPECT_EQ(ms, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &ms));
+ EXPECT_EQ(ms, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &ms));
+ EXPECT_EQ(ms, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &s));
+ EXPECT_EQ(s, 1_s);
+ EXPECT_TRUE(extract("6s"_s, &s));
+ EXPECT_EQ(s, 6_s);
+ EXPECT_TRUE(extract("7min"_s, &s));
+ EXPECT_EQ(s, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &s));
+ EXPECT_EQ(s, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &s));
+ EXPECT_EQ(s, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &min));
+ EXPECT_EQ(min, 1_min);
+ EXPECT_TRUE(extract("7min"_s, &min));
+ EXPECT_EQ(min, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &min));
+ EXPECT_EQ(min, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &min));
+ EXPECT_EQ(min, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &h));
+ EXPECT_EQ(h, 1_h);
+ EXPECT_TRUE(extract("8h"_s, &h));
+ EXPECT_EQ(h, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &h));
+ EXPECT_EQ(h, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &d));
+ EXPECT_EQ(d, 1_d);
+ EXPECT_TRUE(extract("9d"_s, &d));
+ EXPECT_EQ(d, 9_d);
+}
} // namespace tmwa
diff --git a/src/io/fd.cpp b/src/io/fd.cpp
index c0b44e8..bb0bbd5 100644
--- a/src/io/fd.cpp
+++ b/src/io/fd.cpp
@@ -102,6 +102,11 @@ namespace io
int FD::close()
{
+ if (fd == -1)
+ {
+ errno = EBADF;
+ return -1;
+ }
return ::close(fd);
}
int FD::shutdown(int how)
diff --git a/src/io/fd.hpp b/src/io/fd.hpp
index d04d5bf..03a8b44 100644
--- a/src/io/fd.hpp
+++ b/src/io/fd.hpp
@@ -23,8 +23,6 @@
#include <sys/select.h>
#include <sys/socket.h>
-#include "../strings/fwd.hpp"
-
#include "../diagnostics.hpp"
diff --git a/src/io/fwd.hpp b/src/io/fwd.hpp
index deeb08c..3b9452b 100644
--- a/src/io/fwd.hpp
+++ b/src/io/fwd.hpp
@@ -20,6 +20,12 @@
#include "../sanity.hpp"
+#include "../ints/fwd.hpp" // rank 1
+#include "../strings/fwd.hpp" // rank 1
+#include "../compat/fwd.hpp" // rank 2
+#include "../generic/fwd.hpp" // rank 3
+// io/fwd.hpp is rank 4
+
namespace tmwa
{
@@ -28,5 +34,12 @@ namespace io
class ReadFile;
class WriteFile;
class AppendFile;
+ class LineReader;
+ class LineCharReader;
+ class Line;
+ class LineChar;
+ class LineSpan;
+ template<class T>
+ class Spanned;
} // namespace io
} // namespace tmwa
diff --git a/src/io/line.cpp b/src/io/line.cpp
index a1cdf42..5d7e792 100644
--- a/src/io/line.cpp
+++ b/src/io/line.cpp
@@ -19,9 +19,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "../strings/astring.hpp"
-#include "../strings/mstring.hpp"
#include "../strings/zstring.hpp"
-#include "../strings/xstring.hpp"
#include "cxxstdio.hpp"
@@ -32,71 +30,6 @@ namespace tmwa
{
namespace io
{
- AString Line::message_str(ZString cat, ZString msg) const
- {
- MString out;
- if (column)
- out += STRPRINTF("%s:%u:%u: %s: %s\n"_fmt,
- filename, line, column, cat, msg);
- else
- out += STRPRINTF("%s:%u: %s: %s\n"_fmt,
- filename, line, cat, msg);
- out += STRPRINTF("%s\n"_fmt, text);
- out += STRPRINTF("%*c\n"_fmt, column, '^');
- return AString(out);
- }
-
- void Line::message(ZString cat, ZString msg) const
- {
- if (column)
- FPRINTF(stderr, "%s:%u:%u: %s: %s\n"_fmt,
- filename, line, column, cat, msg);
- else
- FPRINTF(stderr, "%s:%u: %s: %s\n"_fmt,
- filename, line, cat, msg);
- FPRINTF(stderr, "%s\n"_fmt, text);
- FPRINTF(stderr, "%*c\n"_fmt, column, '^');
- }
-
- AString LineSpan::message_str(ZString cat, ZString msg) const
- {
- assert (begin.column);
- assert (end.column);
- assert (begin.line < end.line || begin.column <= end.column);
-
- MString out;
- if (begin.line == end.line)
- {
- out += STRPRINTF("%s:%u:%u: %s: %s\n"_fmt,
- begin.filename, begin.line, begin.column, cat, msg);
- out += STRPRINTF("%s\n"_fmt, begin.text);
- out += STRPRINTF("%*c"_fmt, begin.column, '^');
- for (unsigned c = begin.column; c != end.column; ++c)
- out += '~';
- out += '\n';
- }
- else
- {
- out += STRPRINTF("%s:%u:%u: %s: %s\n"_fmt,
- begin.filename, begin.line, begin.column, cat, msg);
- out += STRPRINTF("%s\n"_fmt, begin.text);
- out += STRPRINTF("%*c"_fmt, begin.column, '^');
- for (unsigned c = begin.column; c != begin.text.size(); ++c)
- out += '~';
- out += " ...\n"_s;
- out += STRPRINTF("%s\n"_fmt, end.text);
- for (unsigned c = 0; c != end.column; ++c)
- out += '~';
- out += '\n';
- }
- return AString(out);
- }
-
- void LineSpan::message(ZString cat, ZString msg) const
- {
- FPRINTF(stderr, "%s"_fmt, message_str(cat, msg));
- }
-
LineReader::LineReader(ZString name)
: filename(name), line(0), column(0), rf(name)
{}
@@ -105,6 +38,14 @@ namespace io
: filename(name), line(0), column(0), rf(fd)
{}
+ LineReader::LineReader(read_file_from_string, ZString name, XString content, int startline, FD fd)
+ : filename(name), line(startline-1), column(0), rf(from_string, content, fd)
+ {}
+
+ LineReader::LineReader(read_file_from_string, ZString name, LString content, int startline, FD fd)
+ : filename(name), line(startline-1), column(0), rf(from_string, content, fd)
+ {}
+
bool LineReader::read_line(Line& l)
{
AString text;
@@ -145,6 +86,38 @@ namespace io
column = 0;
}
+ LineCharReader::LineCharReader(read_file_from_string, ZString name, XString content, int startline, int startcol, FD fd)
+ : LineReader(from_string, name, content, 1, fd)
+ {
+ column = 1; // not 0, not whole line
+ if (rf.is_open())
+ adv();
+ if (!line)
+ column = 0;
+ else
+ {
+ line = startline;
+ column = startcol;
+ line_text = STRPRINTF("%*s"_fmt, static_cast<int>(column-1 + line_text.size()), line_text);
+ }
+ }
+
+ LineCharReader::LineCharReader(read_file_from_string, ZString name, LString content, int startline, int startcol, FD fd)
+ : LineReader(from_string, name, content, 1, fd)
+ {
+ column = 1; // not 0, not whole line
+ if (rf.is_open())
+ adv();
+ if (!line)
+ column = 0;
+ else
+ {
+ line = startline;
+ column = startcol;
+ line_text = STRPRINTF("%*s"_fmt, static_cast<int>(column-1 + line_text.size()), line_text);
+ }
+ }
+
bool LineCharReader::get(LineChar& c)
{
if (!column)
diff --git a/src/io/line.hpp b/src/io/line.hpp
index 8244c5e..c94eeb9 100644
--- a/src/io/line.hpp
+++ b/src/io/line.hpp
@@ -21,55 +21,15 @@
#include "fwd.hpp"
#include "../strings/rstring.hpp"
-#include "../strings/zstring.hpp"
-#include "../strings/literal.hpp"
#include "read.hpp"
+#include "span.hpp"
namespace tmwa
{
namespace io
{
- // TODO split this out
- struct Line
- {
- RString text;
-
- RString filename;
- // 1-based
- uint16_t line, column;
-
- AString message_str(ZString cat, ZString msg) const;
- void message(ZString cat, ZString msg) const;
- void note(ZString msg) const { message("note"_s, msg); }
- void warning(ZString msg) const { message("warning"_s, msg); }
- void error(ZString msg) const { message("error"_s, msg); }
- };
-
- // psst, don't tell anyone
- struct LineChar : Line
- {
- char ch()
- {
- size_t c = column - 1;
- if (c == text.size())
- return '\n';
- return text[c];
- }
- };
-
- struct LineSpan
- {
- LineChar begin, end;
-
- AString message_str(ZString cat, ZString msg) const;
- void message(ZString cat, ZString msg) const;
- void note(ZString msg) const { message("note"_s, msg); }
- void warning(ZString msg) const { message("warning"_s, msg); }
- void error(ZString msg) const { message("error"_s, msg); }
- };
-
class LineReader
{
protected:
@@ -83,6 +43,8 @@ namespace io
LineReader& operator = (LineReader&&) = delete;
// needed for unit tests
LineReader(ZString name, FD fd);
+ LineReader(read_file_from_string, ZString name, XString content, int startline=1, FD fd=FD());
+ LineReader(read_file_from_string, ZString name, LString content, int startline=1, FD fd=FD());
bool read_line(Line& l);
bool is_open();
@@ -97,6 +59,8 @@ namespace io
LineCharReader(LineCharReader&&) = delete;
LineCharReader& operator = (LineCharReader&&) = delete;
LineCharReader(ZString name, FD fd);
+ LineCharReader(read_file_from_string, ZString name, XString content, int startline=1, int startcol=1, FD fd=FD());
+ LineCharReader(read_file_from_string, ZString name, LString content, int startline=1, int startcol=1, FD fd=FD());
bool get(LineChar& c);
void adv();
diff --git a/src/io/line_test.cpp b/src/io/line_test.cpp
index edf60bd..582ee81 100644
--- a/src/io/line_test.cpp
+++ b/src/io/line_test.cpp
@@ -23,6 +23,8 @@
#include "../strings/astring.hpp"
#include "../strings/zstring.hpp"
+#include "../tests/fdhack.hpp"
+
#include "../poison.hpp"
@@ -57,6 +59,7 @@ TEST(io, line1)
}
TEST(io, line2)
{
+ QuietFd q;
io::LineReader lr("<string2>"_s, string_pipe("Hello\nWorld"_s));
io::Line hi;
EXPECT_TRUE(lr.read_line(hi));
@@ -73,6 +76,7 @@ TEST(io, line2)
}
TEST(io, line3)
{
+ QuietFd q;
io::LineReader lr("<string3>"_s, string_pipe("Hello\rWorld"_s));
io::Line hi;
EXPECT_TRUE(lr.read_line(hi));
@@ -89,6 +93,7 @@ TEST(io, line3)
}
TEST(io, line4)
{
+ QuietFd q;
io::LineReader lr("<string4>"_s, string_pipe("Hello\r\nWorld"_s));
io::Line hi;
EXPECT_TRUE(lr.read_line(hi));
@@ -105,6 +110,7 @@ TEST(io, line4)
}
TEST(io, line5)
{
+ QuietFd q;
io::LineReader lr("<string5>"_s, string_pipe("Hello\n\rWorld"_s));
io::Line hi;
EXPECT_TRUE(lr.read_line(hi));
@@ -124,6 +130,20 @@ TEST(io, line5)
EXPECT_EQ(hi.column, 0);
EXPECT_FALSE(lr.read_line(hi));
}
+TEST(io, line1text)
+{
+ io::LineReader lr(io::from_string, "<string1text>"_s, "Hello\nWorld"_s, 2);
+ io::Line hi;
+ EXPECT_TRUE(lr.read_line(hi));
+ EXPECT_EQ(hi.text, "Hello"_s);
+ EXPECT_EQ(hi.line, 2);
+ EXPECT_EQ(hi.column, 0);
+ EXPECT_TRUE(lr.read_line(hi));
+ EXPECT_EQ(hi.text, "World"_s);
+ EXPECT_EQ(hi.line, 3);
+ EXPECT_EQ(hi.column, 0);
+ EXPECT_FALSE(lr.read_line(hi));
+}
TEST(io, linechar1)
{
@@ -175,6 +195,7 @@ TEST(io, linechar1)
}
TEST(io, linechar2)
{
+ QuietFd q;
io::LineCharReader lr("<stringchar2>"_s, string_pipe("Hi\nWu"_s));
io::LineChar c;
EXPECT_TRUE(lr.get(c));
@@ -223,6 +244,7 @@ TEST(io, linechar2)
}
TEST(io, linechar3)
{
+ QuietFd q;
io::LineCharReader lr("<stringchar3>"_s, string_pipe("Hi\rWu"_s));
io::LineChar c;
EXPECT_TRUE(lr.get(c));
@@ -271,6 +293,7 @@ TEST(io, linechar3)
}
TEST(io, linechar4)
{
+ QuietFd q;
io::LineCharReader lr("<stringchar4>"_s, string_pipe("Hi\r\nWu"_s));
io::LineChar c;
EXPECT_TRUE(lr.get(c));
@@ -319,6 +342,7 @@ TEST(io, linechar4)
}
TEST(io, linechar5)
{
+ QuietFd q;
io::LineCharReader lr("<stringchar5>"_s, string_pipe("Hi\n\rWu"_s));
io::LineChar c;
EXPECT_TRUE(lr.get(c));
@@ -372,6 +396,42 @@ TEST(io, linechar5)
lr.adv();
EXPECT_FALSE(lr.get(c));
}
+TEST(io, linechar1text)
+{
+ io::LineCharReader lr(io::from_string, "<stringchar1text>"_s, "Hi\nWu\n"_s, 2, 3);
+ io::LineChar c;
+ EXPECT_TRUE(lr.get(c));
+ EXPECT_EQ(c.ch(), 'H');
+ EXPECT_EQ(c.line, 2);
+ EXPECT_EQ(c.column, 3);
+ lr.adv();
+ EXPECT_TRUE(lr.get(c));
+ EXPECT_EQ(c.ch(), 'i');
+ EXPECT_EQ(c.line, 2);
+ EXPECT_EQ(c.column, 4);
+ lr.adv();
+ EXPECT_TRUE(lr.get(c));
+ EXPECT_EQ(c.ch(), '\n');
+ EXPECT_EQ(c.line, 2);
+ EXPECT_EQ(c.column, 5);
+ lr.adv();
+ EXPECT_TRUE(lr.get(c));
+ EXPECT_EQ(c.ch(), 'W');
+ EXPECT_EQ(c.line, 3);
+ EXPECT_EQ(c.column, 1);
+ lr.adv();
+ EXPECT_TRUE(lr.get(c));
+ EXPECT_EQ(c.ch(), 'u');
+ EXPECT_EQ(c.line, 3);
+ EXPECT_EQ(c.column, 2);
+ lr.adv();
+ EXPECT_TRUE(lr.get(c));
+ EXPECT_EQ(c.ch(), '\n');
+ EXPECT_EQ(c.line, 3);
+ EXPECT_EQ(c.column, 3);
+ lr.adv();
+ EXPECT_FALSE(lr.get(c));
+}
TEST(io, linespan)
{
@@ -402,17 +462,17 @@ TEST(io, linespan)
}
while (span.end.ch() != 'r');
- EXPECT_EQ(span.begin.message_str("note"_s, "foo"_s),
+ EXPECT_EQ(span.begin.note_str("foo"_s),
"<span>:1:5: note: foo\n"
"Hello,\n"
" ^\n"_s
);
- EXPECT_EQ(span.end.message_str("warning"_s, "bar"_s),
+ EXPECT_EQ(span.end.warning_str("bar"_s),
"<span>:2:3: warning: bar\n"
"World!\n"
" ^\n"_s
);
- EXPECT_EQ(span.message_str("error"_s, "qux"_s),
+ EXPECT_EQ(span.error_str("qux"_s),
"<span>:1:5: error: qux\n"
"Hello,\n"
" ^~ ...\n"
diff --git a/src/io/read.cpp b/src/io/read.cpp
index 3ae5246..32974d6 100644
--- a/src/io/read.cpp
+++ b/src/io/read.cpp
@@ -1,7 +1,7 @@
#include "read.hpp"
// io/read.cpp - Input from files
//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2013-2014 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -25,7 +25,7 @@
#include "../strings/zstring.hpp"
#include "../strings/literal.hpp"
-#include "../io/cxxstdio.hpp"
+#include "cxxstdio.hpp"
#include "../poison.hpp"
@@ -36,6 +36,8 @@ namespace io
{
ReadFile::ReadFile(FD f)
: fd(f), start(0), end(0)
+ // only for debug-sanity
+ , buf{}
{
}
ReadFile::ReadFile(ZString name)
@@ -46,6 +48,32 @@ namespace io
: fd(dir.open_fd(name, O_RDONLY | O_CLOEXEC)), start(0), end(0)
{
}
+ ReadFile::ReadFile(read_file_from_string, XString content, FD f)
+ : fd(f), start(0), end(), extra()
+ {
+ if (content.size() <= 4096)
+ {
+ end = content.size();
+ auto z = std::copy(content.begin(), content.end(), buf);
+ // only for debug sanity
+ std::fill(z, std::end(buf), 0);
+ return;
+ }
+ auto base = content.base();
+ if (!base)
+ {
+ extra = content;
+ end = content.size();
+ return;
+ }
+ start = &*content.begin() - &*base->begin();
+ end = &*content.end() - &*base->begin();
+ extra = *base;
+ }
+ ReadFile::ReadFile(read_file_from_string, LString content, FD f)
+ : ReadFile(from_string, RString(content), f)
+ {
+ }
ReadFile::~ReadFile()
{
fd.close();
@@ -54,6 +82,14 @@ namespace io
bool ReadFile::get(char& c)
{
+ if (extra)
+ {
+ c = extra[start];
+ ++start;
+ if (start == end)
+ extra = ""_s;
+ return true;
+ }
if (start == end)
{
if (fd == FD())
@@ -82,6 +118,7 @@ namespace io
}
bool ReadFile::getline(AString& line)
{
+ bool was_real_file = fd != FD();
MString tmp;
char c;
bool anything = false;
@@ -115,15 +152,17 @@ 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;
}
bool ReadFile::is_open()
{
- return fd != FD();
+ return fd != FD() || start != end;
}
} // namespace io
} // namespace tmwa
diff --git a/src/io/read.hpp b/src/io/read.hpp
index 1ec26ca..2e3611b 100644
--- a/src/io/read.hpp
+++ b/src/io/read.hpp
@@ -1,7 +1,7 @@
#pragma once
// io/read.hpp - Input from files.
//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2013-2014 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -20,7 +20,7 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
+#include "../strings/rstring.hpp"
#include "dir.hpp"
#include "fd.hpp"
@@ -29,18 +29,28 @@ namespace tmwa
{
namespace io
{
+ enum read_file_from_string
+ {
+ from_string,
+ };
+
+ // TODO - for internal warnings, it would be convenient if this class
+ // didn't exist at all, and instead everything was done with line info.
class ReadFile
{
private:
FD fd;
unsigned short start, end;
char buf[4096];
+ RString extra;
public:
explicit
ReadFile(FD fd);
explicit
ReadFile(ZString name);
ReadFile(const DirFd& dir, ZString name);
+ ReadFile(read_file_from_string, XString content, FD fd=FD());
+ ReadFile(read_file_from_string, LString content, FD fd=FD());
ReadFile& operator = (ReadFile&&) = delete;
ReadFile(ReadFile&&) = delete;
diff --git a/src/io/read_test.cpp b/src/io/read_test.cpp
index 8fe84b7..22c67c8 100644
--- a/src/io/read_test.cpp
+++ b/src/io/read_test.cpp
@@ -24,6 +24,8 @@
#include "../strings/zstring.hpp"
#include "../strings/literal.hpp"
+#include "../tests/fdhack.hpp"
+
#include "../poison.hpp"
@@ -47,6 +49,7 @@ io::FD string_pipe(ZString sz)
TEST(io, read1)
{
+ QuietFd q;
io::ReadFile rf(string_pipe("Hello"_s));
AString hi;
EXPECT_TRUE(rf.getline(hi));
@@ -63,6 +66,7 @@ TEST(io, read2)
}
TEST(io, read3)
{
+ QuietFd q;
io::ReadFile rf(string_pipe("Hello\r"_s));
AString hi;
EXPECT_TRUE(rf.getline(hi));
@@ -71,6 +75,7 @@ TEST(io, read3)
}
TEST(io, read4)
{
+ QuietFd q;
io::ReadFile rf(string_pipe("Hello\r\n"_s));
AString hi;
EXPECT_TRUE(rf.getline(hi));
@@ -79,6 +84,7 @@ TEST(io, read4)
}
TEST(io, read5)
{
+ QuietFd q;
io::ReadFile rf(string_pipe("Hello\n\r"_s));
AString hi;
EXPECT_TRUE(rf.getline(hi));
@@ -87,4 +93,65 @@ TEST(io, read5)
EXPECT_FALSE(hi);
EXPECT_FALSE(rf.getline(hi));
}
+
+#define S15 "0123456789abcde"_s
+#define S16 "0123456789abcdef"_s
+#define S255 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S15
+#define S256 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16
+#define S4095 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S255
+#define S4096 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256
+
+TEST(io, readstringr)
+{
+ LString tests[] =
+ {
+ S15,
+ S16,
+ S255,
+ S256,
+ S4095,
+ S4096,
+ S4096 S16,
+ };
+ for (RString test : tests)
+ {
+ char buf[test.size() + 1];
+
+ io::ReadFile rf(io::from_string, test);
+ EXPECT_EQ(rf.get(buf, sizeof(buf)), test.size());
+ EXPECT_EQ(test, XString(buf + 0, buf + test.size(), nullptr));
+
+ io::ReadFile rf2(io::from_string, test, string_pipe("\na"_s));
+ EXPECT_EQ(rf2.get(buf, sizeof(buf)), test.size() + 1);
+ EXPECT_EQ(test, XString(buf + 0, buf + test.size(), nullptr));
+ EXPECT_EQ('\n', buf[test.size()]);
+ }
+}
+
+TEST(io, readstringx)
+{
+ LString tests[] =
+ {
+ S15,
+ S16,
+ S255,
+ S256,
+ S4095,
+ S4096,
+ S4096 S16,
+ };
+ for (XString test : tests)
+ {
+ char buf[test.size() + 1];
+
+ io::ReadFile rf(io::from_string, test);
+ EXPECT_EQ(rf.get(buf, sizeof(buf)), test.size());
+ EXPECT_EQ(test, XString(buf + 0, buf + test.size(), nullptr));
+
+ io::ReadFile rf2(io::from_string, test, string_pipe("\na"_s));
+ EXPECT_EQ(rf2.get(buf, sizeof(buf)), test.size() + 1);
+ EXPECT_EQ(test, XString(buf + 0, buf + test.size(), nullptr));
+ EXPECT_EQ('\n', buf[test.size()]);
+ }
+}
} // namespace tmwa
diff --git a/src/io/span.cpp b/src/io/span.cpp
new file mode 100644
index 0000000..6d116c7
--- /dev/null
+++ b/src/io/span.cpp
@@ -0,0 +1,113 @@
+#include "span.hpp"
+// io/span.cpp - Tracking info about input
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "../strings/astring.hpp"
+#include "../strings/mstring.hpp"
+#include "../strings/zstring.hpp"
+
+#include "cxxstdio.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace io
+{
+ io::LineSpan Line::to_span() const
+ {
+ io::LineSpan rv;
+ rv.begin.text = this->text;
+ rv.begin.filename = this->filename;
+ rv.begin.line = this->line;
+ rv.begin.column = 1;
+ rv.end.text = this->text;
+ rv.end.filename = this->filename;
+ rv.end.line = this->line;
+ rv.end.column = this->text.size();
+ return rv;
+ }
+
+ AString Line::message_str(ZString cat, ZString msg) const
+ {
+ MString out;
+ if (column)
+ out += STRPRINTF("%s:%u:%u: %s: %s\n"_fmt,
+ filename, line, column, cat, msg);
+ else
+ out += STRPRINTF("%s:%u: %s: %s\n"_fmt,
+ filename, line, cat, msg);
+ out += STRPRINTF("%s\n"_fmt, text);
+ out += STRPRINTF("%*c\n"_fmt, column, '^');
+ return AString(out);
+ }
+
+ void Line::message(ZString cat, ZString msg) const
+ {
+ if (column)
+ FPRINTF(stderr, "%s:%u:%u: %s: %s\n"_fmt,
+ filename, line, column, cat, msg);
+ else
+ FPRINTF(stderr, "%s:%u: %s: %s\n"_fmt,
+ filename, line, cat, msg);
+ FPRINTF(stderr, "%s\n"_fmt, text);
+ FPRINTF(stderr, "%*c\n"_fmt, column, '^');
+ }
+
+ AString LineSpan::message_str(ZString cat, ZString msg) const
+ {
+ assert (begin.column);
+ assert (end.column);
+ assert (begin.line < end.line || begin.column <= end.column);
+
+ MString out;
+ if (begin.line == end.line)
+ {
+ out += STRPRINTF("%s:%u:%u: %s: %s\n"_fmt,
+ begin.filename, begin.line, begin.column, cat, msg);
+ out += STRPRINTF("%s\n"_fmt, begin.text);
+ out += STRPRINTF("%*c"_fmt, begin.column, '^');
+ for (unsigned c = begin.column; c != end.column; ++c)
+ out += '~';
+ out += '\n';
+ }
+ else
+ {
+ out += STRPRINTF("%s:%u:%u: %s: %s\n"_fmt,
+ begin.filename, begin.line, begin.column, cat, msg);
+ out += STRPRINTF("%s\n"_fmt, begin.text);
+ out += STRPRINTF("%*c"_fmt, begin.column, '^');
+ for (unsigned c = begin.column; c != begin.text.size(); ++c)
+ out += '~';
+ out += " ...\n"_s;
+ out += STRPRINTF("%s\n"_fmt, end.text);
+ for (unsigned c = 0; c != end.column; ++c)
+ out += '~';
+ out += '\n';
+ }
+ return AString(out);
+ }
+
+ void LineSpan::message(ZString cat, ZString msg) const
+ {
+ FPRINTF(stderr, "%s"_fmt, message_str(cat, msg));
+ }
+} // namespace io
+} // namespace tmwa
diff --git a/src/io/span.hpp b/src/io/span.hpp
new file mode 100644
index 0000000..9962b7c
--- /dev/null
+++ b/src/io/span.hpp
@@ -0,0 +1,92 @@
+#pragma once
+// io/span.hpp - Tracking info about input
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+#include "../strings/rstring.hpp"
+#include "../strings/zstring.hpp"
+#include "../strings/literal.hpp"
+
+
+namespace tmwa
+{
+namespace io
+{
+ // TODO split this out
+ struct Line
+ {
+ RString text;
+
+ RString filename;
+ // 1-based
+ uint16_t line, column;
+
+ AString message_str(ZString cat, ZString msg) const;
+ AString note_str(ZString msg) const { return message_str("note"_s, msg); }
+ AString warning_str(ZString msg) const { return message_str("warning"_s, msg); }
+ AString error_str(ZString msg) const { return message_str("error"_s, msg); }
+ void message(ZString cat, ZString msg) const;
+ void note(ZString msg) const { message("note"_s, msg); }
+ void warning(ZString msg) const { message("warning"_s, msg); }
+ void error(ZString msg) const { message("error"_s, msg); }
+
+ LineSpan to_span() const;
+ };
+
+ // psst, don't tell anyone
+ struct LineChar : Line
+ {
+ char ch()
+ {
+ size_t c = column - 1;
+ if (c == text.size())
+ return '\n';
+ return text[c];
+ }
+ };
+
+ struct LineSpan
+ {
+ LineChar begin, end;
+
+ AString message_str(ZString cat, ZString msg) const;
+ AString note_str(ZString msg) const { return message_str("note"_s, msg); }
+ AString warning_str(ZString msg) const { return message_str("warning"_s, msg); }
+ AString error_str(ZString msg) const { return message_str("error"_s, msg); }
+ void message(ZString cat, ZString msg) const;
+ void note(ZString msg) const { message("note"_s, msg); }
+ void warning(ZString msg) const { message("warning"_s, msg); }
+ void error(ZString msg) const { message("error"_s, msg); }
+ };
+
+ template<class T>
+ struct Spanned
+ {
+ T data;
+ LineSpan span;
+ };
+
+ template<class T>
+ Spanned<T> respan(LineSpan span, T data)
+ {
+ return Spanned<T>{std::move(data), std::move(span)};
+ }
+} // namespace io
+} // namespace tmwa
diff --git a/src/io/tty.cpp b/src/io/tty.cpp
deleted file mode 100644
index c498740..0000000
--- a/src/io/tty.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#include "tty.hpp"
-// io/tty.cpp - terminal escape sequences
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-/* Nothing to see here, move along */
-} // namespace tmwa
diff --git a/src/io/write.cpp b/src/io/write.cpp
index 5359c7a..a98954b 100644
--- a/src/io/write.cpp
+++ b/src/io/write.cpp
@@ -37,6 +37,8 @@ namespace io
{
WriteFile::WriteFile(FD f, bool linebuffered)
: fd(f), lb(linebuffered), buflen(0)
+ // only for debug-sanity
+ , buf{}
{}
WriteFile::WriteFile(ZString name, bool linebuffered)
: fd(FD::open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)), lb(linebuffered), buflen(0)
diff --git a/src/io/write.hpp b/src/io/write.hpp
index 1ab05f3..d7d3699 100644
--- a/src/io/write.hpp
+++ b/src/io/write.hpp
@@ -22,8 +22,6 @@
#include <cstdarg>
-#include "../strings/fwd.hpp"
-
#include "dir.hpp"
#include "fd.hpp"
diff --git a/src/map/magic-expr-eval.cpp b/src/login/consts.hpp
index 9903600..4af2e41 100644
--- a/src/map/magic-expr-eval.cpp
+++ b/src/login/consts.hpp
@@ -1,5 +1,6 @@
-#include "magic-expr-eval.hpp"
-// magic-expr-eval.cpp - Utilities for evaluating magic.
+
+#pragma once
+// consts.hpp - Constants for tmwa-login.
//
// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
//
@@ -18,12 +19,14 @@
// 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 "../poison.hpp"
+#include "fwd.hpp"
namespace tmwa
{
-namespace magic
+namespace login
{
-} // namespace magic
+constexpr int MAX_SERVERS = 30;
+constexpr int AUTH_FIFO_SIZE = 256;
+} // namespace login
} // namespace tmwa
diff --git a/src/login/fwd.hpp b/src/login/fwd.hpp
index 94fe3c0..6516404 100644
--- a/src/login/fwd.hpp
+++ b/src/login/fwd.hpp
@@ -20,8 +20,28 @@
#include "../sanity.hpp"
+#include "../ints/fwd.hpp" // rank 1
+#include "../strings/fwd.hpp" // rank 1
+#include "../compat/fwd.hpp"
+#include "../generic/fwd.hpp" // rank 3
+#include "../io/fwd.hpp" // rank 4
+#include "../net/fwd.hpp" // rank 5
+#include "../mmo/fwd.hpp" // rank 6
+#include "../proto2/fwd.hpp" // rank 8
+#include "../high/fwd.hpp" // rank 9
+#include "../wire/fwd.hpp" // rank 9
+// login/fwd.hpp is rank ∞ because it is an executable
+
namespace tmwa
{
-// meh, add more when I feel like it
+namespace login
+{
+ struct LoginConf;
+ struct LoginLanConf;
+ struct AuthFifo;
+ struct mmo_char_server;
+ struct AuthData;
+ // meh, add more when I feel like it
+} // namespace login
} // namespace tmwa
diff --git a/src/login/globals.cpp b/src/login/globals.cpp
new file mode 100644
index 0000000..0c8a150
--- /dev/null
+++ b/src/login/globals.cpp
@@ -0,0 +1,54 @@
+#include "globals.hpp"
+// globals.cpp - Evil global variables for tmwa-login.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "../generic/array.hpp"
+#include "../generic/db.hpp"
+
+#include "login.hpp"
+#include "login_conf.hpp"
+#include "login_lan_conf.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+ namespace login
+ {
+ LoginConf login_conf;
+ LoginLanConf login_lan_conf;
+ AccountId account_id_count = START_ACCOUNT_NUM;
+ tick_t creation_time_GM_account_file;
+ Array<mmo_char_server, MAX_SERVERS> server;
+ Array<Session *, MAX_SERVERS> server_session;
+ // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed
+ Array<int, MAX_SERVERS> server_freezeflag;
+ Session *login_session;
+ // minimum level of player/GM (0: player, 1-99: gm) to connect on the server
+ Array<AuthFifo, AUTH_FIFO_SIZE> auth_fifo;
+ // TODO replace with auto_fifo_it
+ int auth_fifo_pos = 0;
+ std::vector<AuthData> auth_data;
+ // TODO make this just be Map<AccountId, GmLevel>
+ Map<AccountId, GM_Account> gm_account_db;
+ // For forked DB writes
+ pid_t pid = 0;
+ } // namespace login
+} // namespace tmwa
diff --git a/src/mmo/consts.cpp b/src/login/globals.hpp
index e49cdf5..541ea98 100644
--- a/src/mmo/consts.cpp
+++ b/src/login/globals.hpp
@@ -1,5 +1,5 @@
-#include "consts.hpp"
-// consts.cpp - empty mess of constants
+#pragma once
+// globals.hpp - Evil global variables for tmwa-login.
//
// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
//
@@ -18,9 +18,31 @@
// 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 "../poison.hpp"
+#include "fwd.hpp"
+
+#include <vector>
+
+#include "../net/timer.t.hpp"
+
+#include "consts.hpp"
namespace tmwa
{
+ namespace login
+ {
+ extern LoginConf login_conf;
+ extern LoginLanConf login_lan_conf;
+ extern AccountId account_id_count;
+ extern tick_t creation_time_GM_account_file;
+ extern Array<mmo_char_server, MAX_SERVERS> server;
+ extern Array<Session *, MAX_SERVERS> server_session;
+ extern Array<int, MAX_SERVERS> server_freezeflag;
+ extern Session *login_session;
+ extern Array<AuthFifo, AUTH_FIFO_SIZE> auth_fifo;
+ extern int auth_fifo_pos;
+ extern std::vector<AuthData> auth_data;
+ extern Map<AccountId, GM_Account> gm_account_db;
+ extern pid_t pid;
+ } // namespace login
} // namespace tmwa
diff --git a/src/login/login.cpp b/src/login/login.cpp
index ccb68fc..92f8cc0 100644
--- a/src/login/login.cpp
+++ b/src/login/login.cpp
@@ -45,23 +45,19 @@
#include "../generic/random.hpp"
#include "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
#include "../io/lock.hpp"
#include "../io/read.hpp"
+#include "../io/span.hpp"
#include "../io/tty.hpp"
#include "../io/write.hpp"
-#include "../net/packets.hpp"
#include "../net/socket.hpp"
#include "../net/timer.hpp"
#include "../mmo/config_parse.hpp"
-#include "../mmo/core.hpp"
-#include "../mmo/extract.hpp"
#include "../mmo/human_time_diff.hpp"
#include "../mmo/ids.hpp"
-#include "../mmo/md5more.hpp"
-#include "../mmo/mmo.hpp"
-#include "../mmo/utils.hpp"
#include "../mmo/version.hpp"
#include "../proto2/any-user.hpp"
@@ -69,115 +65,23 @@
#include "../proto2/login-char.hpp"
#include "../proto2/login-user.hpp"
-#include "../poison.hpp"
-
+#include "../high/core.hpp"
+#include "../high/extract_mmo.hpp"
+#include "../high/md5more.hpp"
+#include "../high/mmo.hpp"
+#include "../high/utils.hpp"
-namespace tmwa
-{
-constexpr int MAX_SERVERS = 30;
+#include "../wire/packets.hpp"
-constexpr AccountId START_ACCOUNT_NUM = wrap<AccountId>(2000000);
-constexpr AccountId END_ACCOUNT_NUM = wrap<AccountId>(100000000);
-
-struct mmo_account
-{
- AccountName userid;
- AccountPass passwd;
- int passwdenc;
+#include "globals.hpp"
+#include "login_conf.hpp"
+#include "login_lan_conf.hpp"
- AccountId account_id;
- int login_id1;
- int login_id2;
- AccountId char_id;
- timestamp_milliseconds_buffer lastlogin;
- SEX sex;
-};
-
-struct mmo_char_server
-{
- ServerName name;
- IP4Address ip;
- uint16_t port;
- uint16_t users;
-};
-
-static
-AccountId account_id_count = START_ACCOUNT_NUM;
-static
-int new_account = 0;
-static
-int login_port = 6900;
-static
-IP4Address lan_char_ip = IP4_LOCALHOST;
-static
-IP4Mask lan_subnet = IP4Mask(IP4_LOCALHOST, IP4_BROADCAST);
-static
-AString update_host;
-static
-AccountName userid;
-static
-AccountPass passwd;
-static
-ServerName main_server;
-
-static
-AString account_filename = "save/account.txt"_s;
-static
-AString gm_account_filename = "save/gm_account.txt"_s;
-static
-AString login_log_filename = "log/login.log"_s;
-static
-AString login_log_unknown_packets_filename = "log/login_unknown_packets.log"_s;
-static
-int save_unknown_packets = 0;
-static
-tick_t creation_time_GM_account_file;
-static
-std::chrono::seconds gm_account_filename_check_timer = 15_s;
-
-static
-int display_parse_login = 0; // 0: no, 1: yes
-static
-int display_parse_admin = 0; // 0: no, 1: yes
-static
-int display_parse_fromchar = 0; // 0: no, 1: yes (without packet 0x2714), 2: all packets
-
-static
-Array<struct mmo_char_server, MAX_SERVERS> server;
-static
-Array<Session *, MAX_SERVERS> server_session;
-static
-Array<int, MAX_SERVERS> server_freezeflag; // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed
-static
-int anti_freeze_enable = 0;
-static
-std::chrono::seconds anti_freeze_interval = 15_s;
+#include "../poison.hpp"
-static
-Session *login_session;
-enum class ACO
+namespace tmwa
{
- DENY_ALLOW,
- ALLOW_DENY,
- MUTUAL_FAILURE,
-};
-
-static
-ACO access_order = ACO::DENY_ALLOW;
-static
-std::vector<IP4Mask>
-access_allow, access_deny, access_ladmin;
-
-static
-GmLevel min_level_to_connect = GmLevel::from(0_u32); // minimum level of player/GM (0: player, 1-99: gm) to connect on the server
-static
-int add_to_unlimited_account = 0; // Give possibility or not to adjust (ladmin command: timeadd) the time of an unlimited account.
-static
-int start_limited_time = -1; // Starting additional sec from now for the limited time at creation of accounts (-1: unlimited time, 0 or more: additional sec from now)
-static
-int check_ip_flag = 1; // It's to check IP of a player between login-server and char-server (part of anti-hacking system)
-
DIAG_PUSH();
DIAG_I(missing_noreturn);
void SessionDeleter::operator()(SessionData *)
@@ -186,67 +90,54 @@ void SessionDeleter::operator()(SessionData *)
}
DIAG_POP();
-constexpr int AUTH_FIFO_SIZE = 256;
-struct AuthFifo
+// out of namespace because ADL is dumb
+bool extract(XString str, login::ACO *aco)
{
- AccountId account_id;
- int login_id1, login_id2;
- IP4Address ip;
- SEX sex;
- int delflag;
-};
-static
-Array<AuthFifo, AUTH_FIFO_SIZE> auth_fifo;
-// TODO replace with auto_fifo_it
-static
-int auth_fifo_pos = 0;
-
-struct AuthData
+ using login::ACO;
+ if (str == "deny,allow"_s || str == "deny, allow"_s)
+ {
+ *aco = ACO::DENY_ALLOW;
+ return true;
+ }
+ if (str == "allow,deny"_s || str == "allow, deny"_s)
+ {
+ *aco = ACO::ALLOW_DENY;
+ return true;
+ }
+ // typo from old config files
+ if (str == "mutual-failture"_s || str == "mutual-failure"_s)
+ {
+ *aco = ACO::MUTUAL_FAILURE;
+ return true;
+ }
+ return false;
+}
+namespace login
+{
+struct mmo_account
{
- AccountId account_id;
- SEX sex;
AccountName userid;
- AccountCrypt pass;
+ AccountPass passwd;
+ int passwdenc;
+
+ AccountId account_id;
+ int login_id1;
+ int login_id2;
+ AccountId char_id;
timestamp_milliseconds_buffer lastlogin;
- int logincount;
- int state; // packet 0x006a value + 1 (0: compte OK)
- AccountEmail email; // e-mail (by default: a@a.com)
- timestamp_seconds_buffer error_message; // Message of error code #6 = Your are Prohibited to log in until %s (packet 0x006a)
- TimeT ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban)
- TimeT connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
- IP4Address last_ip; // save of last IP of connection
- VString<254> memo; // a memo field
- int account_reg2_num;
- Array<GlobalReg, ACCOUNT_REG2_NUM> account_reg2;
+ SEX sex;
};
-static
-std::vector<AuthData> auth_data;
-
-static
-int admin_state = 0;
-static
-AccountPass admin_pass;
-static
-AString gm_pass;
-static
-GmLevel level_new_gm = GmLevel::from(60u);
-
-static
-Map<AccountId, GM_Account> gm_account_db;
-
-static
-pid_t pid = 0; // For forked DB writes
//------------------------------
// Writing function of logs file
//------------------------------
#define LOGIN_LOG(fmt, ...) \
- login_log(STRPRINTF(fmt, ## __VA_ARGS__))
+ tmwa::login::login_log(STRPRINTF(fmt, ## __VA_ARGS__))
static
void login_log(XString line)
{
- io::AppendFile logfp(login_log_filename);
+ io::AppendFile logfp(login_conf.login_log_filename);
if (!logfp.is_open())
return;
log_with_timestamp(logfp, line);
@@ -287,10 +178,8 @@ void delete_admin(Session *s)
static
GmLevel isGM(AccountId account_id)
{
- GM_Account *p = gm_account_db.search(account_id);
- if (p == nullptr)
- return GmLevel();
- return p->level;
+ Option<P<GM_Account>> p = gm_account_db.search(account_id);
+ return TRY_UNWRAP(p, return GmLevel())->level;
}
//-------------------------------------------------------
@@ -303,16 +192,16 @@ int read_gm_account(void)
gm_account_db.clear();
- creation_time_GM_account_file = file_modified(gm_account_filename);
+ creation_time_GM_account_file = file_modified(login_conf.gm_account_filename);
- io::ReadFile fp(gm_account_filename);
+ io::ReadFile fp(login_conf.gm_account_filename);
if (!fp.is_open())
{
PRINTF("read_gm_account: GM accounts file [%s] not found.\n"_fmt,
- gm_account_filename);
+ login_conf.gm_account_filename);
PRINTF(" Actually, there is no GM accounts on the server.\n"_fmt);
LOGIN_LOG("read_gm_account: GM accounts file [%s] not found.\n"_fmt,
- gm_account_filename);
+ login_conf.gm_account_filename);
LOGIN_LOG(" Actually, there is no GM accounts on the server.\n"_fmt);
return 1;
}
@@ -326,7 +215,7 @@ int read_gm_account(void)
GM_Account p {};
if (!extract(line, record<' '>(&p.account_id, &p.level)))
PRINTF("read_gm_account: file [%s], invalid 'id_acount level' format: '%s'\n"_fmt,
- gm_account_filename, line);
+ login_conf.gm_account_filename, line);
else
{
GmLevel GM_level = isGM(p.account_id);
@@ -356,9 +245,9 @@ int read_gm_account(void)
}
PRINTF("read_gm_account: file '%s' readed (%d GM accounts found).\n"_fmt,
- gm_account_filename, c);
+ login_conf.gm_account_filename, c);
LOGIN_LOG("read_gm_account: file '%s' readed (%d GM accounts found).\n"_fmt,
- gm_account_filename, c);
+ login_conf.gm_account_filename, c);
return 0;
}
@@ -377,29 +266,29 @@ bool check_ip(IP4Address ip)
};
ACF flag = ACF::DEF;
- if (access_allow.empty() && access_deny.empty())
+ if (login_conf.allow.empty() && login_conf.deny.empty())
return 1;
// When there is no restriction, all IP are authorised.
- if (std::find_if(access_allow.begin(), access_allow.end(),
+ if (std::find_if(login_conf.allow.begin(), login_conf.allow.end(),
[&ip](IP4Mask m)
{
return m.covers(ip);
- }) != access_allow.end())
+ }) != login_conf.allow.end())
{
{
flag = ACF::ALLOW;
- if (access_order == ACO::ALLOW_DENY)
+ if (login_conf.order == ACO::ALLOW_DENY)
// With 'allow, deny' (deny if not allow), allow has priority
return 1;
}
}
- if (std::find_if(access_deny.begin(), access_deny.end(),
+ if (std::find_if(login_conf.deny.begin(), login_conf.deny.end(),
[&ip](IP4Mask m)
{
return m.covers(ip);
- }) != access_deny.end())
+ }) != login_conf.deny.end())
{
{
flag = ACF::DENY;
@@ -408,7 +297,7 @@ bool check_ip(IP4Address ip)
}
}
- return flag == ACF::ALLOW || access_order == ACO::DENY_ALLOW;
+ return flag == ACF::ALLOW || login_conf.order == ACO::DENY_ALLOW;
// With 'mutual-failture', only 'allow' and non 'deny' IP are authorised.
// A non 'allow' (even non 'deny') IP is not authorised. It's like: if allowed and not denied, it's authorised.
// So, it's disapproval if you have no description at the time of 'mutual-failture'.
@@ -421,15 +310,15 @@ bool check_ip(IP4Address ip)
static
bool check_ladminip(IP4Address ip)
{
- if (access_ladmin.empty())
+ if (login_conf.ladminallowip.empty())
// When there is no restriction, all IP are authorised.
return true;
- return std::find_if(access_ladmin.begin(), access_ladmin.end(),
+ return std::find_if(login_conf.ladminallowip.begin(), login_conf.ladminallowip.end(),
[&ip](IP4Mask m)
{
return m.covers(ip);
- }) != access_ladmin.end();
+ }) != login_conf.ladminallowip.end();
}
//-----------------------------------------------
@@ -480,7 +369,7 @@ AString mmo_auth_tostr(const AuthData *p)
p->state,
p->email,
p->error_message,
- p->connect_until_time,
+ TimeT(),
p->last_ip,
p->memo,
p->ban_until_time);
@@ -495,8 +384,9 @@ AString mmo_auth_tostr(const AuthData *p)
}
static
-bool extract(XString line, AuthData *ad)
+bool impl_extract(XString line, AuthData *ad)
{
+ TimeT unused_connect_until_time;
std::vector<GlobalReg> vars;
VString<1> sex;
VString<15> ip;
@@ -511,7 +401,7 @@ bool extract(XString line, AuthData *ad)
&ad->state,
&ad->email,
&ad->error_message,
- &ad->connect_until_time,
+ &unused_connect_until_time,
&ip,
&ad->memo,
&ad->ban_until_time,
@@ -577,13 +467,13 @@ int mmo_auth_init(void)
{
int gm_count = 0;
- io::ReadFile in(account_filename);
+ io::ReadFile in(login_conf.account_filename);
if (!in.is_open())
{
// no account file -> no account -> no login, including char-server (ERROR)
// not anymore! :-)
PRINTF(SGR_BOLD SGR_RED "mmo_auth_init: Accounts file [%s] not found." SGR_RESET "\n"_fmt,
- account_filename);
+ login_conf.account_filename);
return 0;
}
@@ -620,7 +510,7 @@ int mmo_auth_init(void)
}
AString str = STRPRINTF("%s has %zu accounts (%d GMs)\n"_fmt,
- account_filename, auth_data.size(), gm_count);
+ login_conf.account_filename, auth_data.size(), gm_count);
PRINTF("mmo_auth_init: %s\n"_fmt, str);
LOGIN_LOG("%s\n"_fmt, line);
@@ -633,7 +523,7 @@ int mmo_auth_init(void)
static
void mmo_auth_sync(void)
{
- io::WriteLock fp(account_filename);
+ io::WriteLock fp(login_conf.account_filename);
if (!fp.is_open())
{
@@ -643,7 +533,7 @@ void mmo_auth_sync(void)
FPRINTF(fp,
"// Accounts file: here are saved all information about the accounts.\n"_fmt);
FPRINTF(fp,
- "// Structure: ID, account name, password, last login time, sex, # of logins, state, email, error message for state 7, validity time, last (accepted) login ip, memo field, ban timestamp, repeated(register text, register value)\n"_fmt);
+ "// Structure: ID, account name, password, last login time, sex, # of logins, state, email, error message for state 7, validity time (unused), last (accepted) login ip, memo field, ban timestamp, repeated(register text, register value)\n"_fmt);
FPRINTF(fp, "// Some explanations:\n"_fmt);
FPRINTF(fp,
"// account name : between 4 to 23 char for a normal account (standard client can't send less than 4 char).\n"_fmt);
@@ -754,11 +644,11 @@ static
void check_GM_file(TimerData *, tick_t)
{
// if we would not check
- if (gm_account_filename_check_timer == interval_t::zero())
+ if (login_conf.gm_account_filename_check_timer == interval_t::zero())
return;
// get last modify time/date
- tick_t new_time = file_modified(gm_account_filename);
+ tick_t new_time = file_modified(login_conf.gm_account_filename);
if (new_time != creation_time_GM_account_file)
{
@@ -795,17 +685,6 @@ AccountId mmo_auth_new(struct mmo_account *account, SEX sex, AccountEmail email)
ad.error_message = stringish<timestamp_seconds_buffer>("-"_s);
ad.ban_until_time = TimeT();
- if (start_limited_time < 0)
- ad.connect_until_time = TimeT(); // unlimited
- else
- {
- // limited time
- TimeT timestamp = static_cast<time_t>(TimeT::now()) + start_limited_time;
- // there used to be a silly overflow check here, but it wasn't
- // correct, and we don't support time-limited accounts.
- ad.connect_until_time = timestamp;
- }
-
ad.last_ip = IP4Address();
ad.memo = "!"_s;
ad.account_reg2_num = 0;
@@ -827,7 +706,7 @@ int mmo_auth(struct mmo_account *account, Session *s)
// Account creation with _M/_F
if (account->passwdenc == 0
&& (account->userid.endswith("_F"_s) || account->userid.endswith("_M"_s))
- && new_account == 1 && account_id_count < END_ACCOUNT_NUM
+ && login_conf.new_account && account_id_count < END_ACCOUNT_NUM
&& (account->userid.size() - 2) >= 4 && account->passwd.size() >= 4)
{
new_account_sex = account->userid.back();
@@ -899,14 +778,6 @@ int mmo_auth(struct mmo_account *account, Session *s)
}
}
- if (ad->connect_until_time
- && ad->connect_until_time < TimeT::now())
- {
- LOGIN_LOG("Connection refused (account: %s, expired ID, ip: %s)\n"_fmt,
- account->userid, ip);
- return 2; // 2 = This ID is expired
- }
-
LOGIN_LOG("Authentification accepted (account: %s (id: %d), ip: %s)\n"_fmt,
account->userid, ad->account_id, ip);
}
@@ -989,28 +860,12 @@ void parse_fromchar(Session *s)
uint16_t packet_id;
while (rv == RecvResult::Complete && packet_peek_id(s, &packet_id))
{
- if (display_parse_fromchar == 2 || (display_parse_fromchar == 1 && packet_id != 0x2714)) // 0x2714 is done very often (number of players)
+ if (login_conf.display_parse_fromchar == 2 || (login_conf.display_parse_fromchar == 1 && packet_id != 0x2714)) // 0x2714 is done very often (number of players)
PRINTF("parse_fromchar: connection #%d, packet: 0x%x (with being read: %zu bytes).\n"_fmt,
s, packet_id, packet_avail(s));
switch (packet_id)
{
- // request from map-server via char-server to reload GM accounts (by Yor).
- case 0x2709:
- {
- Packet_Fixed<0x2709> fixed;
- rv = recv_fpacket<0x2709, 2>(s, fixed);
- if (rv != RecvResult::Complete)
- break;
-
- LOGIN_LOG("Char-server '%s': Request to re-load GM configuration file (ip: %s).\n"_fmt,
- server[id].name, ip);
- read_gm_account();
- // send GM accounts to all char-servers
- send_GM_accounts();
- break;
- }
-
case 0x2712: // request from char-server to authentify an account
{
Packet_Fixed<0x2712> fixed;
@@ -1027,8 +882,7 @@ void parse_fromchar(Session *s)
auth_fifo[i].login_id1 == fixed.login_id1 &&
auth_fifo[i].login_id2 == fixed.login_id2 && // relate to the versions higher than 18
auth_fifo[i].sex == fixed.sex &&
- (!check_ip_flag
- || auth_fifo[i].ip == fixed.ip)
+ auth_fifo[i].ip == fixed.ip
&& !auth_fifo[i].delflag)
{
auth_fifo[i].delflag = 1;
@@ -1057,7 +911,6 @@ void parse_fromchar(Session *s)
fixed_13.account_id = acc;
fixed_13.invalid = 0;
fixed_13.email = ad.email;
- fixed_13.connect_until = ad.connect_until_time;
send_fpacket<0x2713, 51>(s, fixed_13);
break;
@@ -1092,7 +945,7 @@ void parse_fromchar(Session *s)
break;
server[id].users = fixed.users;
- if (anti_freeze_enable)
+ if (login_conf.anti_freeze_enable)
server_freezeflag[id] = 5; // Char anti-freeze system. Counter. 5 ok, 4...0 freezed
break;
}
@@ -1116,7 +969,6 @@ void parse_fromchar(Session *s)
Packet_Fixed<0x2717> fixed_17;
fixed_17.account_id = account_id;
fixed_17.email = ad.email;
- fixed_17.connect_until = ad.connect_until_time;
send_fpacket<0x2717, 50>(s, fixed_17);
if (rv != RecvResult::Complete)
@@ -1130,93 +982,6 @@ void parse_fromchar(Session *s)
break;
}
- case 0x2720: // To become GM request
- {
- Packet_Head<0x2720> head;
- AString repeat;
- rv = recv_vpacket<0x2720, 8, 1>(s, head, repeat);
- if (rv != RecvResult::Complete)
- break;
-
- {
- AccountId acc = head.account_id;
-
- Packet_Fixed<0x2721> fixed_21;
- fixed_21.account_id = acc;
- fixed_21.gm_level = GmLevel();
-
- AString pass = repeat;
-
- if (pass == gm_pass)
- {
- // only non-GM can become GM
- if (!isGM(acc))
- {
- // if we autorise creation
- if (level_new_gm)
- {
- // if we can open the file to add the new GM
- io::AppendFile fp(gm_account_filename);
- if (fp.is_open())
- {
- timestamp_seconds_buffer tmpstr;
- stamp_time(tmpstr);
- FPRINTF(fp,
- "\n// %s: @GM command on account %d\n%d %d\n"_fmt,
- tmpstr,
- acc, acc, level_new_gm);
- if (!fp.close())
- {
- PRINTF("warning: didn't actually save GM file\n"_fmt);
- }
- fixed_21.gm_level = level_new_gm;
- read_gm_account();
- send_GM_accounts();
- PRINTF("GM Change of the account %d: level 0 -> %d.\n"_fmt,
- acc, level_new_gm);
- LOGIN_LOG("Char-server '%s': GM Change of the account %d: level 0 -> %d (ip: %s).\n"_fmt,
- server[id].name, acc,
- level_new_gm, ip);
- }
- else
- {
- PRINTF("Error of GM change (suggested account: %d, correct password, unable to add a GM account in GM accounts file)\n"_fmt,
- acc);
- LOGIN_LOG("Char-server '%s': Error of GM change (suggested account: %d, correct password, unable to add a GM account in GM accounts file, ip: %s).\n"_fmt,
- server[id].name, acc, ip);
- }
- }
- else
- {
- PRINTF("Error of GM change (suggested account: %d, correct password, but GM creation is disable (level_new_gm = 0))\n"_fmt,
- acc);
- LOGIN_LOG("Char-server '%s': Error of GM change (suggested account: %d, correct password, but GM creation is disable (level_new_gm = 0), ip: %s).\n"_fmt,
- server[id].name, acc, ip);
- }
- }
- else
- {
- PRINTF("Error of GM change (suggested account: %d (already GM), correct password).\n"_fmt,
- acc);
- LOGIN_LOG("Char-server '%s': Error of GM change (suggested account: %d (already GM), correct password, ip: %s).\n"_fmt,
- server[id].name, acc, ip);
- }
- }
- else
- {
- PRINTF("Error of GM change (suggested account: %d, invalid password).\n"_fmt,
- acc);
- LOGIN_LOG("Char-server '%s': Error of GM change (suggested account: %d, invalid password, ip: %s).\n"_fmt,
- server[id].name, acc, ip);
- }
- for (Session *ss : iter_char_sessions())
- {
- send_fpacket<0x2721, 10>(ss, fixed_21);
- }
- }
- break;
- }
-
// Map server send information to change an email of an account via char-server
case 0x2722: // 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B
{
@@ -1603,19 +1368,17 @@ void parse_fromchar(Session *s)
default:
{
- io::AppendFile logfp(login_log_unknown_packets_filename);
- if (logfp.is_open())
{
timestamp_milliseconds_buffer timestr;
stamp_time(timestr);
- FPRINTF(logfp,
+ FPRINTF(stderr,
"%s: receiving of an unknown packet -> disconnection\n"_fmt,
timestr);
- FPRINTF(logfp,
+ FPRINTF(stderr,
"parse_fromchar: connection #%d (ip: %s), packet: 0x%x (with being read: %zu).\n"_fmt,
s, ip, packet_id, packet_avail(s));
- FPRINTF(logfp, "Detail (in hex):\n"_fmt);
- packet_dump(logfp, s);
+ FPRINTF(stderr, "Detail (in hex):\n"_fmt);
+ packet_dump(s);
}
PRINTF("parse_fromchar: Unknown packet 0x%x (from a char-server)! -> disconnection.\n"_fmt,
packet_id);
@@ -1641,7 +1404,7 @@ void parse_admin(Session *s)
uint16_t packet_id;
while (rv == RecvResult::Complete && packet_peek_id(s, &packet_id))
{
- if (display_parse_admin == 1)
+ if (login_conf.display_parse_admin)
PRINTF("parse_admin: connection #%d, packet: 0x%x (with being read: %zu bytes).\n"_fmt,
s, packet_id, packet_avail(s));
@@ -1718,23 +1481,6 @@ void parse_admin(Session *s)
break;
}
- case 0x7924:
- { // [Fate] Itemfrob package: change item IDs
- Packet_Fixed<0x7924> fixed;
- rv = recv_fpacket<0x7924, 10>(s, fixed);
- if (rv != RecvResult::Complete)
- break;
-
- for (Session *ss : iter_char_sessions())
- {
- send_fpacket<0x7924, 10>(ss, fixed);
- }
-
- Packet_Fixed<0x7925> fixed_25;
- send_fpacket<0x7925, 2>(s, fixed_25);
- break;
- }
-
case 0x7930: // Request for an account creation
{
Packet_Fixed<0x7930> fixed;
@@ -2096,10 +1842,10 @@ void parse_admin(Session *s)
AccountId GM_account;
GmLevel GM_level;
int modify_flag;
- io::WriteLock fp2(gm_account_filename);
+ io::WriteLock fp2(login_conf.gm_account_filename);
if (fp2.is_open())
{
- io::ReadFile fp(gm_account_filename);
+ io::ReadFile fp(login_conf.gm_account_filename);
if (fp.is_open())
{
timestamp_seconds_buffer tmpstr;
@@ -2330,48 +2076,6 @@ void parse_admin(Session *s)
break;
}
- case 0x7948: // Request to change the validity limit (timestamp) (absolute value)
- {
- Packet_Fixed<0x7948> fixed;
- rv = recv_fpacket<0x7948, 30>(s, fixed);
- if (rv != RecvResult::Complete)
- break;
-
- Packet_Fixed<0x7949> fixed_49;
- {
- fixed_49.account_id = AccountId();
- AccountName account_name = stringish<AccountName>(fixed.account_name.to_print());
- TimeT timestamp = fixed.valid_until;
- timestamp_seconds_buffer tmpstr = stringish<timestamp_seconds_buffer>("unlimited"_s);
- if (timestamp)
- stamp_time(tmpstr, &timestamp);
- AuthData *ad = search_account(account_name);
- if (ad)
- {
- fixed_49.account_name = ad->userid;
- LOGIN_LOG("'ladmin': Change of a validity limit (account: %s, new validity: %lld (%s), ip: %s)\n"_fmt,
- ad->userid,
- timestamp,
- tmpstr,
- ip);
- ad->connect_until_time = timestamp;
- fixed_49.account_id = ad->account_id;
- }
- else
- {
- fixed_49.account_name = account_name;
- LOGIN_LOG("'ladmin': Attempt to change the validity limit of an unknown account (account: %s, received validity: %lld (%s), ip: %s)\n"_fmt,
- account_name,
- timestamp,
- tmpstr,
- ip);
- }
- fixed_49.valid_until = timestamp;
- }
- send_fpacket<0x7949, 34>(s, fixed_49);
- break;
- }
-
case 0x794a: // Request to change the final date of a banishment (timestamp) (absolute value)
{
Packet_Fixed<0x794a> fixed;
@@ -2475,7 +2179,7 @@ void parse_admin(Session *s)
timestamp_seconds_buffer tmpstr = stringish<timestamp_seconds_buffer>("no banishment"_s);
if (timestamp)
stamp_time(tmpstr, &timestamp);
- LOGIN_LOG("'ladmin': Adjustment of a final date of a banishment (account: %s, (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %lld (%s), ip: %s)\n"_fmt,
+ LOGIN_LOG("'ladmin': Adjustment of a final date of a banishment (account: %s, (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new ban: %lld (%s), ip: %s)\n"_fmt,
ad->userid,
ban_diff.year, ban_diff.month,
ban_diff.day, ban_diff.hour,
@@ -2583,99 +2287,6 @@ void parse_admin(Session *s)
break;
}
- case 0x7950: // Request to change the validity limite (timestamp) (relative change)
- {
- Packet_Fixed<0x7950> fixed;
- rv = recv_fpacket<0x7950, 38>(s, fixed);
- if (rv != RecvResult::Complete)
- break;
-
- Packet_Fixed<0x7951> fixed_51;
- {
- fixed_51.account_id = AccountId();
- AccountName account_name = stringish<AccountName>(fixed.account_name.to_print());
- AuthData *ad = search_account(account_name);
- if (ad)
- {
- fixed_51.account_id = ad->account_id;
- fixed_51.account_name = ad->userid;
- if (add_to_unlimited_account == 0 && !ad->connect_until_time)
- {
- LOGIN_LOG("'ladmin': Attempt to adjust the validity limit of an unlimited account (account: %s, ip: %s)\n"_fmt,
- ad->userid, ip);
- fixed_51.valid_until = TimeT();
- }
- else
- {
- TimeT now = TimeT::now();
- TimeT timestamp = ad->connect_until_time;
- if (!timestamp || timestamp < now)
- timestamp = now;
- struct tm tmtime = timestamp;
- HumanTimeDiff v_diff = fixed.valid_add;
- tmtime.tm_year += v_diff.year;
- tmtime.tm_mon += v_diff.month;
- tmtime.tm_mday += v_diff.day;
- tmtime.tm_hour += v_diff.hour;
- tmtime.tm_min += v_diff.minute;
- tmtime.tm_sec += v_diff.second;
- timestamp = tmtime;
- if (timestamp.okay())
- {
- timestamp_seconds_buffer tmpstr = stringish<timestamp_seconds_buffer>("unlimited"_s);
- timestamp_seconds_buffer tmpstr2 = stringish<timestamp_seconds_buffer>("unlimited"_s);
- if (ad->connect_until_time)
- stamp_time(tmpstr, &ad->connect_until_time);
- if (timestamp)
- stamp_time(tmpstr2, &timestamp);
- LOGIN_LOG("'ladmin': Adjustment of a validity limit (account: %s, %lld (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %lld (%s), ip: %s)\n"_fmt,
- ad->userid,
- ad->connect_until_time,
- tmpstr,
- v_diff.year,
- v_diff.month,
- v_diff.day,
- v_diff.hour,
- v_diff.minute,
- v_diff.second,
- timestamp,
- tmpstr2,
- ip);
- ad->connect_until_time = timestamp;
- fixed_51.valid_until = timestamp;
- }
- else
- {
- timestamp_seconds_buffer tmpstr = stringish<timestamp_seconds_buffer>("unlimited"_s);
- if (ad->connect_until_time)
- stamp_time(tmpstr, &ad->connect_until_time);
- LOGIN_LOG("'ladmin': Impossible to adjust a validity limit (account: %s, %lld (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)\n"_fmt,
- ad->userid,
- ad->connect_until_time,
- tmpstr,
- v_diff.year,
- v_diff.month,
- v_diff.day,
- v_diff.hour,
- v_diff.minute,
- v_diff.second,
- ip);
- fixed_51.valid_until = TimeT();
- }
- }
- }
- else
- {
- fixed_51.account_name = account_name;
- LOGIN_LOG("'ladmin': Attempt to adjust the validity limit of an unknown account (account: %s, ip: %s)\n"_fmt,
- account_name, ip);
- fixed_51.valid_until = TimeT();
- }
- }
- send_fpacket<0x7951, 34>(s, fixed_51);
- break;
- }
-
case 0x7952: // Request about informations of an account (by account name)
{
Packet_Fixed<0x7952> fixed;
@@ -2699,7 +2310,6 @@ void parse_admin(Session *s)
head_53.last_login_string = ad->lastlogin;
head_53.ip_string = convert_for_printf(ad->last_ip);
head_53.email = ad->email;
- head_53.connect_until = ad->connect_until_time;
head_53.ban_until = ad->ban_until_time;
XString repeat_53 = ad->memo;
@@ -2745,7 +2355,6 @@ void parse_admin(Session *s)
head_53.last_login_string = ad.lastlogin;
head_53.ip_string = convert_for_printf(ad.last_ip);
head_53.email = ad.email;
- head_53.connect_until = ad.connect_until_time;
head_53.ban_until = ad.ban_until_time;
XString repeat_53 = ad.memo;
send_vpacket<0x7953, 150, 1>(s, head_53, repeat_53);
@@ -2779,19 +2388,17 @@ void parse_admin(Session *s)
default:
{
- io::AppendFile logfp(login_log_unknown_packets_filename);
- if (logfp.is_open())
{
timestamp_milliseconds_buffer timestr;
stamp_time(timestr);
- FPRINTF(logfp,
+ FPRINTF(stderr,
"%s: receiving of an unknown packet -> disconnection\n"_fmt,
timestr);
- FPRINTF(logfp,
+ FPRINTF(stderr,
"parse_admin: connection #%d (ip: %s), packet: 0x%x (with being read: %zu).\n"_fmt,
s, ip, packet_id, packet_avail(s));
- FPRINTF(logfp, "Detail (in hex):\n"_fmt);
- packet_dump(logfp, s);
+ FPRINTF(stderr, "Detail (in hex):\n"_fmt);
+ packet_dump(s);
}
LOGIN_LOG("'ladmin': End of connection, unknown packet (ip: %s)\n"_fmt,
ip);
@@ -2812,7 +2419,7 @@ void parse_admin(Session *s)
static
bool lan_ip_check(IP4Address p)
{
- bool lancheck = lan_subnet.covers(p);
+ bool lancheck = login_lan_conf.lan_subnet.covers(p);
PRINTF("LAN test (result): %s.\n"_fmt,
(lancheck) ? SGR_BOLD SGR_CYAN "LAN source" SGR_RESET ""_s : SGR_BOLD SGR_GREEN "WAN source" SGR_RESET ""_s);
@@ -2833,7 +2440,7 @@ void parse_login(Session *s)
uint16_t packet_id;
while (rv == RecvResult::Complete && packet_peek_id(s, &packet_id))
{
- if (display_parse_login == 1)
+ if (login_conf.display_parse_login)
{
if (packet_id == 0x64)
{
@@ -2895,10 +2502,10 @@ void parse_login(Session *s)
if (result == -1)
{
GmLevel gm_level = isGM(account.account_id);
- if (!(gm_level.satisfies(min_level_to_connect)))
+ if (!(gm_level.satisfies(login_conf.min_level_to_connect)))
{
LOGIN_LOG("Connection refused: the minimum GM level for connection is %d (account: %s, GM level: %d, ip: %s).\n"_fmt,
- min_level_to_connect, account.userid,
+ login_conf.min_level_to_connect, account.userid,
gm_level, ip);
Packet_Fixed<0x0081> fixed_81;
fixed_81.error_code = 1; // 01 = Server closed
@@ -2928,9 +2535,9 @@ void parse_login(Session *s)
*/
// if (version_2 & VERSION_2_UPDATEHOST)
{
- if (update_host)
+ if (login_conf.update_host)
{
- send_packet_repeatonly<0x0063, 4, 1>(s, update_host);
+ send_packet_repeatonly<0x0063, 4, 1>(s, login_conf.update_host);
}
}
@@ -2943,7 +2550,7 @@ void parse_login(Session *s)
{
Packet_Repeat<0x0069> info;
if (lan_ip_check(ip))
- info.ip = lan_char_ip;
+ info.ip = login_lan_conf.lan_char_ip;
else
info.ip = server[i].ip;
info.port = server[i].port;
@@ -3043,11 +2650,11 @@ void parse_login(Session *s)
ServerName server_name = stringish<ServerName>(fixed.server_name.to_print());
LOGIN_LOG("Connection request of the char-server '%s' @ %s:%d (ip: %s)\n"_fmt,
server_name, fixed.ip, fixed.port, ip);
- if (account.userid == userid && account.passwd == passwd)
+ if (account.userid == login_conf.userid && account.passwd == login_conf.passwd)
{
// If this is the main server, and we don't already have a main server
if (!server_session[0]
- && server_name == main_server)
+ && server_name == login_conf.main_server)
{
account.account_id = wrap<AccountId>(0_u32);
goto x2710_okay;
@@ -3082,7 +2689,7 @@ void parse_login(Session *s)
//maintenance = RFIFOW(fd, 82);
//is_new = RFIFOW(fd, 84);
server_session[unwrap<AccountId>(account.account_id)] = s;
- if (anti_freeze_enable)
+ if (login_conf.anti_freeze_enable)
server_freezeflag[unwrap<AccountId>(account.account_id)] = 5; // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed
Packet_Fixed<0x2711> fixed_11;
@@ -3124,7 +2731,7 @@ void parse_login(Session *s)
Packet_Fixed<0x7531> fixed_31;
Version version = CURRENT_LOGIN_SERVER_VERSION;
- version.flags = new_account ? 1 : 0;
+ version.flags = login_conf.new_account ? 1 : 0;
fixed_31.version = version;
send_fpacket<0x7531, 10>(s, fixed_31);
break;
@@ -3163,8 +2770,8 @@ void parse_login(Session *s)
// non encrypted password
AccountPass password = stringish<AccountPass>(fixed.account_pass.to_print());
// If remote administration is enabled and password sent by client matches password read from login server configuration file
- if ((admin_state == 1)
- && (password == admin_pass))
+ if (login_conf.admin_state
+ && password == login_conf.admin_pass)
{
LOGIN_LOG("'ladmin'-login: Connection in administration mode accepted (non encrypted password: %s, ip: %s)\n"_fmt,
password, ip);
@@ -3172,7 +2779,7 @@ void parse_login(Session *s)
fixed_19.error = 0;
s->set_parsers(SessionParsers{.func_parse= parse_admin, .func_delete= delete_admin});
}
- else if (admin_state != 1)
+ else if (!login_conf.admin_state)
LOGIN_LOG("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (non encrypted password: %s, ip: %s)\n"_fmt,
password, ip);
else
@@ -3194,22 +2801,19 @@ void parse_login(Session *s)
default:
{
- if (save_unknown_packets)
{
- io::AppendFile logfp(login_log_unknown_packets_filename);
- if (logfp.is_open())
{
timestamp_milliseconds_buffer timestr;
stamp_time(timestr);
- FPRINTF(logfp,
+ FPRINTF(stderr,
"%s: receiving of an unknown packet -> disconnection\n"_fmt,
timestr);
- FPRINTF(logfp,
+ FPRINTF(stderr,
"parse_login: connection #%d (ip: %s), packet: 0x%x (with being read: %zu).\n"_fmt,
s, ip, packet_id,
packet_avail(s));
- FPRINTF(logfp, "Detail (in hex):\n"_fmt);
- packet_dump(logfp, s);
+ FPRINTF(stderr, "Detail (in hex):\n"_fmt);
+ packet_dump(s);
}
}
LOGIN_LOG("End of connection, unknown packet (ip: %s)\n"_fmt, ip);
@@ -3222,67 +2826,19 @@ void parse_login(Session *s)
s->set_eof();
}
-//----------------------------------
-// Reading Lan Support configuration
-//----------------------------------
-static
-bool login_lan_config(XString w1, ZString w2)
-{
- struct hostent *h = nullptr;
-
- {
- if (w1 == "lan_char_ip"_s)
- {
- // Read Char-Server Lan IP Address
- h = gethostbyname(w2.c_str());
- if (h != nullptr)
- {
- lan_char_ip = IP4Address({
- static_cast<uint8_t>(h->h_addr[0]),
- static_cast<uint8_t>(h->h_addr[1]),
- static_cast<uint8_t>(h->h_addr[2]),
- static_cast<uint8_t>(h->h_addr[3]),
- });
- }
- else
- {
- PRINTF("Bad IP value: %s\n"_fmt, w2);
- return false;
- }
- PRINTF("LAN IP of char-server: %s.\n"_fmt, lan_char_ip);
- }
- else if (w1 == "subnet"_s /*backward compatibility*/
- || w1 == "lan_subnet"_s)
- {
- if (!extract(w2, &lan_subnet))
- {
- PRINTF("Bad IP mask: %s\n"_fmt, w2);
- return false;
- }
- PRINTF("Sub-network of the char-server: %s.\n"_fmt,
- lan_subnet);
- }
- else
- {
- return false;
- }
- }
- return true;
-}
-
static
bool lan_check()
{
// log the LAN configuration
LOGIN_LOG("The LAN configuration of the server is set:\n"_fmt);
- LOGIN_LOG("- with LAN IP of char-server: %s.\n"_fmt, lan_char_ip);
+ LOGIN_LOG("- with LAN IP of char-server: %s.\n"_fmt, login_lan_conf.lan_char_ip);
LOGIN_LOG("- with the sub-network of the char-server: %s.\n"_fmt,
- lan_subnet);
+ login_lan_conf.lan_subnet);
// sub-network check of the char-server
{
PRINTF("LAN test of LAN IP of the char-server: "_fmt);
- if (!lan_ip_check(lan_char_ip))
+ if (!lan_ip_check(login_lan_conf.lan_char_ip))
{
PRINTF(SGR_BOLD SGR_RED "***ERROR: LAN IP of the char-server doesn't belong to the specified Sub-network"_fmt SGR_RESET "\n");
LOGIN_LOG("***ERROR: LAN IP of the char-server doesn't belong to the specified Sub-network.\n"_fmt);
@@ -3293,229 +2849,6 @@ bool lan_check()
return true;
}
-//-----------------------------------
-// Reading general configuration file
-//-----------------------------------
-static
-bool login_config(XString w1, ZString w2)
-{
- {
- if (w1 == "admin_state"_s)
- {
- admin_state = config_switch(w2);
- }
- else if (w1 == "admin_pass"_s)
- {
- admin_pass = stringish<AccountPass>(w2);
- }
- else if (w1 == "ladminallowip"_s)
- {
- if (w2 == "clear"_s)
- {
- access_ladmin.clear();
- }
- else
- {
- // a.b.c.d/0.0.0.0 (canonically, 0.0.0.0/0) covers all
- if (w2 == "all"_s)
- {
- // reset all previous values
- access_ladmin.clear();
- // set to all
- access_ladmin.push_back(IP4Mask());
- }
- else if (w2
- && !(access_ladmin.size() == 1
- && access_ladmin.front().mask() == IP4Address()))
- {
- // don't add IP if already 'all'
- IP4Mask n;
- if (!extract(w2, &n))
- {
- PRINTF("Bad IP mask: %s\n"_fmt, w2);
- return false;
- }
- access_ladmin.push_back(n);
- }
- }
- }
- else if (w1 == "gm_pass"_s)
- {
- gm_pass = w2;
- }
- else if (w1 == "level_new_gm"_s)
- {
- level_new_gm = GmLevel::from(static_cast<uint32_t>(atoi(w2.c_str())));
- }
- else if (w1 == "new_account"_s)
- {
- new_account = config_switch(w2);
- }
- else if (w1 == "login_port"_s)
- {
- login_port = atoi(w2.c_str());
- }
- else if (w1 == "account_filename"_s)
- {
- account_filename = w2;
- }
- else if (w1 == "gm_account_filename"_s)
- {
- gm_account_filename = w2;
- }
- else if (w1 == "gm_account_filename_check_timer"_s)
- {
- gm_account_filename_check_timer = std::chrono::seconds(atoi(w2.c_str()));
- }
- else if (w1 == "login_log_filename"_s)
- {
- login_log_filename = w2;
- }
- else if (w1 == "login_log_unknown_packets_filename"_s)
- {
- login_log_unknown_packets_filename = w2;
- }
- else if (w1 == "save_unknown_packets"_s)
- {
- save_unknown_packets = config_switch(w2);
- }
- else if (w1 == "display_parse_login"_s)
- {
- display_parse_login = config_switch(w2); // 0: no, 1: yes
- }
- else if (w1 == "display_parse_admin"_s)
- {
- display_parse_admin = config_switch(w2); // 0: no, 1: yes
- }
- else if (w1 == "display_parse_fromchar"_s)
- {
- display_parse_fromchar = config_switch(w2); // 0: no, 1: yes (without packet 0x2714), 2: all packets
- }
- else if (w1 == "min_level_to_connect"_s)
- {
- min_level_to_connect = GmLevel::from(static_cast<uint32_t>(atoi(w2.c_str())));
- }
- else if (w1 == "add_to_unlimited_account"_s)
- {
- add_to_unlimited_account = config_switch(w2);
- }
- else if (w1 == "start_limited_time"_s)
- {
- start_limited_time = atoi(w2.c_str());
- }
- else if (w1 == "check_ip_flag"_s)
- {
- check_ip_flag = config_switch(w2);
- }
- else if (w1 == "order"_s)
- {
- if (w2 == "deny,allow"_s || w2 == "deny, allow"_s)
- access_order = ACO::DENY_ALLOW;
- else if (w2 == "allow,deny"_s || w2 == "allow, deny"_s)
- access_order = ACO::ALLOW_DENY;
- else if (w2 == "mutual-failture"_s || w2 == "mutual-failure"_s)
- access_order = ACO::MUTUAL_FAILURE;
- else
- {
- PRINTF("Bad order: %s\n"_fmt, w2);
- return false;
- }
- }
- else if (w1 == "allow"_s)
- {
- if (w2 == "clear"_s)
- {
- access_allow.clear();
- }
- else
- {
- if (w2 == "all"_s)
- {
- // reset all previous values
- access_allow.clear();
- // set to all
- access_allow.push_back(IP4Mask());
- }
- else if (w2
- && !(access_allow.size() == 1
- && access_allow.front().mask() == IP4Address()))
- {
- // don't add IP if already 'all'
- IP4Mask n;
- if (!extract(w2, &n))
- {
- PRINTF("Bad IP mask: %s\n"_fmt, w2);
- return false;
- }
- access_allow.push_back(n);
- }
- }
- }
- else if (w1 == "deny"_s)
- {
- if (w2 == "clear"_s)
- {
- access_deny.clear();
- }
- else
- {
- if (w2 == "all"_s)
- {
- // reset all previous values
- access_deny.clear();
- // set to all
- access_deny.push_back(IP4Mask());
- }
- else if (w2
- && !(access_deny.size() == 1
- && access_deny.front().mask() == IP4Address()))
- {
- // don't add IP if already 'all'
- IP4Mask n;
- if (!extract(w2, &n))
- {
- PRINTF("Bad IP mask: %s\n"_fmt, w2);
- return false;
- }
- access_deny.push_back(n);
- }
- }
- }
- else if (w1 == "anti_freeze_enable"_s)
- {
- anti_freeze_enable = config_switch(w2);
- }
- else if (w1 == "anti_freeze_interval"_s)
- {
- anti_freeze_interval = std::max(
- std::chrono::seconds(atoi(w2.c_str())),
- 5_s);
- }
- else if (w1 == "update_host"_s)
- {
- update_host = w2;
- }
- else if (w1 == "main_server"_s)
- {
- main_server = stringish<ServerName>(w2);
- }
- else if (w1 == "userid"_s)
- {
- userid = stringish<AccountName>(w2);
- }
- else if (w1 == "passwd"_s)
- {
- passwd = stringish<AccountPass>(w2);
- }
- else
- {
- return false;
- }
- }
-
- return true;
-}
-
//-------------------------------------
// Displaying of configuration warnings
//-------------------------------------
@@ -3523,135 +2856,60 @@ static
bool display_conf_warnings(void)
{
bool rv = true;
- if (admin_state != 0 && admin_state != 1)
- {
- PRINTF("***WARNING: Invalid value for admin_state parameter -> set to 0 (no remote admin).\n"_fmt);
- admin_state = 0;
- rv = false;
- }
- if (admin_state == 1)
+ if (login_conf.admin_state)
{
- if (!admin_pass)
+ if (!login_conf.admin_pass)
{
PRINTF("***WARNING: Administrator password is void (admin_pass).\n"_fmt);
rv = false;
}
- else if (admin_pass == stringish<AccountPass>("admin"_s))
+ else if (login_conf.admin_pass == stringish<AccountPass>("admin"_s))
{
PRINTF("***WARNING: You are using the default administrator password (admin_pass).\n"_fmt);
PRINTF(" We highly recommend that you change it.\n"_fmt);
}
}
- if (!gm_pass)
+ if (!login_conf.gm_pass)
{
PRINTF("***WARNING: 'To GM become' password is void (gm_pass).\n"_fmt);
PRINTF(" We highly recommend that you set one password.\n"_fmt);
rv = false;
}
- else if (gm_pass == "gm"_s)
+ else if (login_conf.gm_pass == "gm"_s)
{
PRINTF("***WARNING: You are using the default GM password (gm_pass).\n"_fmt);
PRINTF(" We highly recommend that you change it.\n"_fmt);
}
- if (new_account != 0 && new_account != 1)
- {
- PRINTF("***WARNING: Invalid value for new_account parameter -> set to 0 (no new account).\n"_fmt);
- new_account = 0;
- rv = false;
- }
-
- if (login_port < 1024 || login_port > 65535)
- {
- PRINTF("***WARNING: Invalid value for login_port parameter -> set to 6900 (default).\n"_fmt);
- login_port = 6900;
- rv = false;
- }
-
- if (gm_account_filename_check_timer.count() < 0)
+ if (login_conf.gm_account_filename_check_timer.count() < 0)
{
PRINTF("***WARNING: Invalid value for gm_account_filename_check_timer parameter.\n"_fmt);
PRINTF(" -> set to 15 sec (default).\n"_fmt);
- gm_account_filename_check_timer = 15_s;
+ login_conf.gm_account_filename_check_timer = 15_s;
rv = false;
}
- else if (gm_account_filename_check_timer == 1_s)
+ else if (login_conf.gm_account_filename_check_timer == 1_s)
{
PRINTF("***WARNING: Invalid value for gm_account_filename_check_timer parameter.\n"_fmt);
PRINTF(" -> set to 2 sec (minimum value).\n"_fmt);
- gm_account_filename_check_timer = 2_s;
- rv = false;
- }
-
- if (save_unknown_packets != 0 && save_unknown_packets != 1)
- {
- PRINTF("WARNING: Invalid value for save_unknown_packets parameter -> set to 0-no save.\n"_fmt);
- save_unknown_packets = 0;
- rv = false;
- }
-
- if (display_parse_login != 0 && display_parse_login != 1)
- { // 0: no, 1: yes
- PRINTF("***WARNING: Invalid value for display_parse_login parameter\n"_fmt);
- PRINTF(" -> set to 0 (no display).\n"_fmt);
- display_parse_login = 0;
- rv = false;
- }
-
- if (display_parse_admin != 0 && display_parse_admin != 1)
- { // 0: no, 1: yes
- PRINTF("***WARNING: Invalid value for display_parse_admin parameter\n"_fmt);
- PRINTF(" -> set to 0 (no display).\n"_fmt);
- display_parse_admin = 0;
- rv = false;
- }
-
- if (display_parse_fromchar < 0 || display_parse_fromchar > 2)
- { // 0: no, 1: yes (without packet 0x2714), 2: all packets
- PRINTF("***WARNING: Invalid value for display_parse_fromchar parameter\n"_fmt);
- PRINTF(" -> set to 0 (no display).\n"_fmt);
- display_parse_fromchar = 0;
- rv = false;
- }
-
- if (add_to_unlimited_account != 0 && add_to_unlimited_account != 1)
- { // 0: no, 1: yes
- PRINTF("***WARNING: Invalid value for add_to_unlimited_account parameter\n"_fmt);
- PRINTF(" -> set to 0 (impossible to add a time to an unlimited account).\n"_fmt);
- add_to_unlimited_account = 0;
- rv = false;
- }
-
- if (start_limited_time < -1)
- { // -1: create unlimited account, 0 or more: additionnal sec from now to create limited time
- PRINTF("***WARNING: Invalid value for start_limited_time parameter\n"_fmt);
- PRINTF(" -> set to -1 (new accounts are created with unlimited time).\n"_fmt);
- start_limited_time = -1;
+ login_conf.gm_account_filename_check_timer = 2_s;
rv = false;
}
- if (check_ip_flag != 0 && check_ip_flag != 1)
- { // 0: no, 1: yes
- PRINTF("***WARNING: Invalid value for check_ip_flag parameter\n"_fmt);
- PRINTF(" -> set to 1 (check players ip between login-server & char-server).\n"_fmt);
- check_ip_flag = 1;
- rv = false;
- }
-
- if (access_order == ACO::DENY_ALLOW)
+ if (login_conf.order == ACO::DENY_ALLOW)
{
- if (access_deny.size() == 1 && access_deny.front().mask() == IP4Address())
+ if (login_conf.deny.size() == 1 && login_conf.deny.front().mask() == IP4Address())
{
PRINTF("***WARNING: The IP security order is 'deny,allow' (allow if not deny).\n"_fmt);
PRINTF(" And you refuse ALL IP.\n"_fmt);
rv = false;
}
}
- else if (access_order == ACO::ALLOW_DENY)
+ else if (login_conf.order == ACO::ALLOW_DENY)
{
- if (access_allow.empty())
+ if (login_conf.allow.empty())
{
PRINTF("***WARNING: The IP security order is 'allow,deny' (deny if not allow).\n"_fmt);
PRINTF(" But, NO IP IS AUTHORISED!\n"_fmt);
@@ -3661,14 +2919,14 @@ bool display_conf_warnings(void)
else
{
// ACO::MUTUAL_FAILURE
- if (access_allow.empty())
+ if (login_conf.allow.empty())
{
PRINTF("***WARNING: The IP security order is 'mutual-failture'\n"_fmt);
PRINTF(" (allow if in the allow list and not in the deny list).\n"_fmt);
PRINTF(" But, NO IP IS AUTHORISED!\n"_fmt);
rv = false;
}
- else if (access_deny.size() == 1 && access_deny.front().mask() == IP4Address())
+ else if (login_conf.deny.size() == 1 && login_conf.deny.front().mask() == IP4Address())
{
PRINTF("***WARNING: The IP security order is mutual-failture\n"_fmt);
PRINTF(" (allow if in the allow list and not in the deny list).\n"_fmt);
@@ -3692,195 +2950,188 @@ void save_config_in_log(void)
// save configuration in log file
LOGIN_LOG("The configuration of the server is set:\n"_fmt);
- if (admin_state != 1)
+ if (!login_conf.admin_state)
LOGIN_LOG("- with no remote administration.\n"_fmt);
- else if (!admin_pass)
+ else if (!login_conf.admin_pass)
LOGIN_LOG("- with a remote administration with a VOID password.\n"_fmt);
- else if (admin_pass == stringish<AccountPass>("admin"_s))
+ else if (login_conf.admin_pass == stringish<AccountPass>("admin"_s))
LOGIN_LOG("- with a remote administration with the DEFAULT password.\n"_fmt);
else
LOGIN_LOG("- with a remote administration with the password of %zu character(s).\n"_fmt,
- admin_pass.size());
- if (access_ladmin.empty()
- || (access_ladmin.size() == 1 && access_ladmin.front().mask() == IP4Address()))
+ login_conf.admin_pass.size());
+ if (login_conf.ladminallowip.empty()
+ || (login_conf.ladminallowip.size() == 1 && login_conf.ladminallowip.front().mask() == IP4Address()))
{
LOGIN_LOG("- to accept any IP for remote administration\n"_fmt);
}
else
{
LOGIN_LOG("- to accept following IP for remote administration:\n"_fmt);
- for (const IP4Mask& ae : access_ladmin)
+ for (const IP4Mask& ae : login_conf.ladminallowip)
LOGIN_LOG(" %s\n"_fmt, ae);
}
- if (!gm_pass)
+ if (!login_conf.gm_pass)
LOGIN_LOG("- with a VOID 'To GM become' password (gm_pass).\n"_fmt);
- else if (gm_pass == "gm"_s)
+ else if (login_conf.gm_pass == "gm"_s)
LOGIN_LOG("- with the DEFAULT 'To GM become' password (gm_pass).\n"_fmt);
else
LOGIN_LOG("- with a 'To GM become' password (gm_pass) of %zu character(s).\n"_fmt,
- gm_pass.size());
- if (!level_new_gm)
+ login_conf.gm_pass.size());
+ if (!login_conf.level_new_gm)
LOGIN_LOG("- to refuse any creation of GM with @gm.\n"_fmt);
else
LOGIN_LOG("- to create GM with level '%d' when @gm is used.\n"_fmt,
- level_new_gm);
+ login_conf.level_new_gm);
- if (new_account == 1)
+ if (login_conf.new_account)
LOGIN_LOG("- to ALLOW new users (with _F/_M).\n"_fmt);
else
LOGIN_LOG("- to NOT ALLOW new users (with _F/_M).\n"_fmt);
- LOGIN_LOG("- with port: %d.\n"_fmt, login_port);
+ LOGIN_LOG("- with port: %d.\n"_fmt, login_conf.login_port);
LOGIN_LOG("- with the accounts file name: '%s'.\n"_fmt,
- account_filename);
+ login_conf.account_filename);
LOGIN_LOG("- with the GM accounts file name: '%s'.\n"_fmt,
- gm_account_filename);
- if (gm_account_filename_check_timer == interval_t::zero())
+ login_conf.gm_account_filename);
+ if (login_conf.gm_account_filename_check_timer == interval_t::zero())
LOGIN_LOG("- to NOT check GM accounts file modifications.\n"_fmt);
else
LOGIN_LOG("- to check GM accounts file modifications every %lld seconds.\n"_fmt,
- maybe_cast<long long>(gm_account_filename_check_timer.count()));
+ maybe_cast<long long>(login_conf.gm_account_filename_check_timer.count()));
// not necessary to log the 'login_log_filename', we are inside :)
- LOGIN_LOG("- with the unknown packets file name: '%s'.\n"_fmt,
- login_log_unknown_packets_filename);
- if (save_unknown_packets)
- LOGIN_LOG("- to SAVE all unkown packets.\n"_fmt);
- else
- LOGIN_LOG("- to SAVE only unkown packets sending by a char-server or a remote administration.\n"_fmt);
- if (display_parse_login)
+ if (login_conf.display_parse_login)
LOGIN_LOG("- to display normal parse packets on console.\n"_fmt);
else
LOGIN_LOG("- to NOT display normal parse packets on console.\n"_fmt);
- if (display_parse_admin)
+ if (login_conf.display_parse_admin)
LOGIN_LOG("- to display administration parse packets on console.\n"_fmt);
else
LOGIN_LOG("- to NOT display administration parse packets on console.\n"_fmt);
- if (display_parse_fromchar)
+ if (login_conf.display_parse_fromchar)
LOGIN_LOG("- to display char-server parse packets on console.\n"_fmt);
else
LOGIN_LOG("- to NOT display char-server parse packets on console.\n"_fmt);
- if (!min_level_to_connect) // 0: all players, 1-99 at least gm level x
+ if (!login_conf.min_level_to_connect) // 0: all players, 1-99 at least gm level x
LOGIN_LOG("- with no minimum level for connection.\n"_fmt);
else
LOGIN_LOG("- to accept only GM with level %d or more.\n"_fmt,
- min_level_to_connect);
-
- if (add_to_unlimited_account)
- LOGIN_LOG("- to authorize adjustment (with timeadd ladmin) on an unlimited account.\n"_fmt);
- else
- LOGIN_LOG("- to refuse adjustment (with timeadd ladmin) on an unlimited account. You must use timeset (ladmin command) before.\n"_fmt);
+ login_conf.min_level_to_connect);
- if (start_limited_time < 0)
- LOGIN_LOG("- to create new accounts with an unlimited time.\n"_fmt);
- else if (start_limited_time == 0)
- LOGIN_LOG("- to create new accounts with a limited time: time of creation.\n"_fmt);
- else
- LOGIN_LOG("- to create new accounts with a limited time: time of creation + %d second(s).\n"_fmt,
- start_limited_time);
-
- if (check_ip_flag)
- LOGIN_LOG("- with control of players IP between login-server and char-server.\n"_fmt);
- else
- LOGIN_LOG("- to not check players IP between login-server and char-server.\n"_fmt);
-
- if (access_order == ACO::DENY_ALLOW)
+ if (login_conf.order == ACO::DENY_ALLOW)
{
- if (access_deny.empty())
+ if (login_conf.deny.empty())
{
LOGIN_LOG("- with the IP security order: 'deny,allow' (allow if not deny). You refuse no IP.\n"_fmt);
}
- else if (access_deny.size() == 1 && access_deny.front().mask() == IP4Address())
+ else if (login_conf.deny.size() == 1 && login_conf.deny.front().mask() == IP4Address())
{
LOGIN_LOG("- with the IP security order: 'deny,allow' (allow if not deny). You refuse ALL IP.\n"_fmt);
}
else
{
LOGIN_LOG("- with the IP security order: 'deny,allow' (allow if not deny). Refused IP are:\n"_fmt);
- for (IP4Mask ae : access_deny)
+ for (IP4Mask ae : login_conf.deny)
LOGIN_LOG(" %s\n"_fmt, ae);
}
}
- else if (access_order == ACO::ALLOW_DENY)
+ else if (login_conf.order == ACO::ALLOW_DENY)
{
- if (access_allow.empty())
+ if (login_conf.allow.empty())
{
LOGIN_LOG("- with the IP security order: 'allow,deny' (deny if not allow). But, NO IP IS AUTHORISED!\n"_fmt);
}
- else if (access_allow.size() == 1 && access_allow.front().mask() == IP4Address())
+ else if (login_conf.allow.size() == 1 && login_conf.allow.front().mask() == IP4Address())
{
LOGIN_LOG("- with the IP security order: 'allow,deny' (deny if not allow). You authorise ALL IP.\n"_fmt);
}
else
{
LOGIN_LOG("- with the IP security order: 'allow,deny' (deny if not allow). Authorised IP are:\n"_fmt);
- for (IP4Mask ae : access_allow)
+ for (IP4Mask ae : login_conf.allow)
LOGIN_LOG(" %s\n"_fmt, ae);
}
}
else
{ // ACO_MUTUAL_FAILTURE
LOGIN_LOG("- with the IP security order: 'mutual-failture' (allow if in the allow list and not in the deny list).\n"_fmt);
- if (access_allow.empty())
+ if (login_conf.allow.empty())
{
LOGIN_LOG(" But, NO IP IS AUTHORISED!\n"_fmt);
}
- else if (access_deny.size() == 1 && access_deny.front().mask() == IP4Address())
+ else if (login_conf.deny.size() == 1 && login_conf.deny.front().mask() == IP4Address())
{
LOGIN_LOG(" But, you refuse ALL IP!\n"_fmt);
}
else
{
- if (access_allow.size() == 1 && access_allow.front().mask() == IP4Address())
+ if (login_conf.allow.size() == 1 && login_conf.allow.front().mask() == IP4Address())
{
LOGIN_LOG(" You authorise ALL IP.\n"_fmt);
}
else
{
LOGIN_LOG(" Authorised IP are:\n"_fmt);
- for (IP4Mask ae : access_allow)
+ for (IP4Mask ae : login_conf.allow)
LOGIN_LOG(" %s\n"_fmt, ae);
}
LOGIN_LOG(" Refused IP are:\n"_fmt);
- for (IP4Mask ae : access_deny)
+ for (IP4Mask ae : login_conf.deny)
LOGIN_LOG(" %s\n"_fmt, ae);
}
}
}
+static
+bool login_config(io::Spanned<XString> key, io::Spanned<ZString> value)
+{
+ return parse_login_conf(login_conf, key, value);
+}
+
+static
+bool login_lan_config(io::Spanned<XString> key, io::Spanned<ZString> value)
+{
+ return parse_login_lan_conf(login_lan_conf, key, value);
+}
+
+static
+bool login_confs(io::Spanned<XString> key, io::Spanned<ZString> value)
+{
+ if (key.data == "login_conf"_s)
+ {
+ return load_config_file(value.data, login_config);
+ }
+ if (key.data == "login_lan_conf"_s)
+ {
+ return load_config_file(value.data, login_lan_config);
+ }
+ key.span.error("Unknown meta-key for login server"_s);
+ return false;
+}
+} // namespace login
+
//--------------------------------------
// Function called at exit of the server
//--------------------------------------
void term_func(void)
{
- mmo_auth_sync();
+ login::mmo_auth_sync();
- auth_data.clear();
- gm_account_db.clear();
- for (int i = 0; i < MAX_SERVERS; i++)
+ login::auth_data.clear();
+ login::gm_account_db.clear();
+ for (int i = 0; i < login::MAX_SERVERS; i++)
{
- Session *s = server_session[i];
+ Session *s = login::server_session[i];
if (s)
delete_session(s);
}
- delete_session(login_session);
+ delete_session(login::login_session);
LOGIN_LOG("----End of login-server (normal end with closing of all files).\n"_fmt);
}
-static
-bool login_confs(XString key, ZString value)
-{
- unsigned sum = 0;
- sum += login_config(key, value);
- sum += login_lan_config(key, value);
- if (sum >= 2)
- abort();
- return sum;
-}
-
//------------------------------
// Main function of login-server
//------------------------------
@@ -3913,59 +3164,60 @@ int do_init(Slice<ZString> argv)
else
{
loaded_config_yet = true;
- runflag &= load_config_file(argvi, login_confs);
+ runflag &= load_config_file(argvi, login::login_confs);
}
}
if (!loaded_config_yet)
- runflag &= load_config_file("conf/tmwa-login.conf"_s, login_confs);
+ runflag &= load_config_file("conf/tmwa-login.conf"_s, login::login_confs);
// not in login_config_read, because we can use 'import' option, and display same message twice or more
// (why is that bad?)
- runflag &= display_conf_warnings();
+ runflag &= login::display_conf_warnings();
// not before, because log file name can be changed
// (that doesn't stop the char-server though)
- save_config_in_log();
- runflag &= lan_check();
+ login::save_config_in_log();
+ runflag &= login::lan_check();
- for (int i = 0; i < AUTH_FIFO_SIZE; i++)
- auth_fifo[i].delflag = 1;
- for (int i = 0; i < MAX_SERVERS; i++)
- server_session[i] = nullptr;
+ for (int i = 0; i < login::AUTH_FIFO_SIZE; i++)
+ login::auth_fifo[i].delflag = 1;
+ for (int i = 0; i < login::MAX_SERVERS; i++)
+ login::server_session[i] = nullptr;
- read_gm_account();
- mmo_auth_init();
+ login::read_gm_account();
+ login::mmo_auth_init();
// set_termfunc (mmo_auth_sync);
- login_session = make_listen_port(login_port, SessionParsers{.func_parse= parse_login, .func_delete= delete_login});
+ login::login_session = make_listen_port(login::login_conf.login_port, SessionParsers{.func_parse= login::parse_login, .func_delete= login::delete_login});
Timer(gettick() + 5_min,
- check_auth_sync,
+ login::check_auth_sync,
5_min
).detach();
- if (anti_freeze_enable > 0)
+ if (login::login_conf.anti_freeze_enable > 0)
{
Timer(gettick() + 1_s,
- char_anti_freeze_system,
- anti_freeze_interval
+ login::char_anti_freeze_system,
+ login::login_conf.anti_freeze_interval
).detach();
}
// add timer to check GM accounts file modification
- std::chrono::seconds j = gm_account_filename_check_timer;
+ std::chrono::seconds j = login::login_conf.gm_account_filename_check_timer;
if (j == interval_t::zero())
j = 1_min;
Timer(gettick() + j,
- check_GM_file,
+ login::check_GM_file,
j).detach();
LOGIN_LOG("The login-server is ready (Server is listening on the port %d).\n"_fmt,
- login_port);
+ login::login_conf.login_port);
PRINTF("The login-server is " SGR_BOLD SGR_GREEN "ready" SGR_RESET " (Server is listening on the port %d).\n\n"_fmt,
- login_port);
+ login::login_conf.login_port);
return 0;
}
+// namespace login ends before term_func and do_init
} // namespace tmwa
diff --git a/src/login/login.hpp b/src/login/login.hpp
index 92f3c76..ae99558 100644
--- a/src/login/login.hpp
+++ b/src/login/login.hpp
@@ -18,11 +18,69 @@
// 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 "login.t.hpp"
+
#include "fwd.hpp"
-#include "login.t.hpp"
+#include "../strings/vstring.hpp"
+
+#include "../compat/time_t.hpp"
+
+#include "../generic/array.hpp"
+
+#include "../net/ip.hpp"
+#include "../net/timestamp-utils.hpp"
+
+#include "../mmo/consts.hpp"
+#include "../mmo/enums.hpp"
+#include "../mmo/ids.hpp"
+#include "../mmo/strs.hpp"
+
+#include "../proto2/net-GlobalReg.hpp"
+
+#include "../high/mmo.hpp"
namespace tmwa
{
+namespace login
+{
+constexpr AccountId START_ACCOUNT_NUM = wrap<AccountId>(2000000);
+constexpr AccountId END_ACCOUNT_NUM = wrap<AccountId>(100000000);
+
+struct AuthData
+{
+ AccountId account_id;
+ SEX sex;
+ AccountName userid;
+ AccountCrypt pass;
+ timestamp_milliseconds_buffer lastlogin;
+ int logincount;
+ int state; // packet 0x006a value + 1 (0: compte OK)
+ AccountEmail email; // e-mail (by default: a@a.com)
+ timestamp_seconds_buffer error_message; // Message of error code #6 = Your are Prohibited to log in until %s (packet 0x006a)
+ TimeT ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban)
+ IP4Address last_ip; // save of last IP of connection
+ VString<254> memo; // a memo field
+ int account_reg2_num;
+ Array<GlobalReg, ACCOUNT_REG2_NUM> account_reg2;
+};
+
+struct mmo_char_server
+{
+ ServerName name;
+ IP4Address ip;
+ uint16_t port;
+ uint16_t users;
+};
+
+struct AuthFifo
+{
+ AccountId account_id;
+ int login_id1, login_id2;
+ IP4Address ip;
+ SEX sex;
+ int delflag;
+};
+} // namespace login
} // namespace tmwa
diff --git a/src/login/login.t.hpp b/src/login/login.t.hpp
index f2c775a..9be0620 100644
--- a/src/login/login.t.hpp
+++ b/src/login/login.t.hpp
@@ -1,9 +1,10 @@
#pragma once
-// login.t.hpp - externally useful types from login
+// login.t.hpp - Types for the login server
//
// Copyright © ????-2004 Athena Dev Teams
// Copyright © 2004-2011 The Mana World Development Team
// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2014 MadCamel
//
// This file is part of The Mana World (Athena server)
//
@@ -22,23 +23,18 @@
#include "fwd.hpp"
-#include <cstdint>
-
-#include "../generic/enum.hpp"
-
namespace tmwa
{
-namespace e
+namespace login
{
-enum class VERSION_2 : uint8_t
+enum class ACO
{
- /// client supports updatehost
- UPDATEHOST = 0x01,
- /// send servers in forward order
- SERVERORDER = 0x02,
+ DENY_ALLOW,
+ ALLOW_DENY,
+ MUTUAL_FAILURE,
};
-ENUM_BITWISE_OPERATORS(VERSION_2)
-}
-using e::VERSION_2;
+} // namespace login
+// out of namespace because ADL is dumb
+bool extract(XString str, login::ACO *aco);
} // namespace tmwa
diff --git a/src/login/main.cpp b/src/login/main.cpp
index 48a471a..4495bda 100644
--- a/src/login/main.cpp
+++ b/src/login/main.cpp
@@ -17,7 +17,7 @@
// 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 "../mmo/core.hpp"
+#include "../high/core.hpp"
#include "login.hpp"
diff --git a/src/main-gdb-head.py b/src/main-gdb-head.py
index 2dac471..a465c97 100644
--- a/src/main-gdb-head.py
+++ b/src/main-gdb-head.py
@@ -29,22 +29,55 @@ def get_basic_type(type_):
type_ = type_.strip_typedefs()
return type_.unqualified()
+def info_symbol(addr):
+ ''' returns (symbol, offset, section, lib or None) or None?
+ '''
+ info = gdb.execute('info symbol %d' % addr, to_string=True)
+ try:
+ sym_and_off, sec_and_lib = info.split(' in section ')
+ except ValueError:
+ return None
+ try:
+ sym, off = sym_and_off.split(' + ')
+ except ValueError:
+ sym = sym_and_off
+ off = 0
+ else:
+ off = int(off, 10)
+ try:
+ sec, lib = sec_and_lib.split(' of ')
+ except ValueError:
+ sec = sec_and_lib
+ lib = None
+ return (sym, off, sec, lib)
+
def finish():
- global finish, initial_globals, FastPrinters, EnumPrinter
+ global finish, initial_globals, FastPrinters, EnumPrinter, PointerPrinter
final_globals = {id(v):v for v in globals().values()}
diff = set(final_globals.keys()) - set(initial_globals.keys()) \
- - {'finish', 'initial_globals', 'FastPrinters'}
+ - {
+ 'finish',
+ 'initial_globals',
+ 'FastPrinters',
+ 'EnumPrinter',
+ 'PointerPrinter',
+ }
fp = FastPrinters()
ep = EnumPrinter
+ ptrp = PointerPrinter
# After this, don't access any more globals in this function.
- del finish, initial_globals, FastPrinters, EnumPrinter
+ del finish, initial_globals, FastPrinters, EnumPrinter, PointerPrinter
for i in diff:
v = final_globals[i]
if hasattr(v, 'children') or hasattr(v, 'to_string'):
fp.add_printer(v)
+ # TODO see if there's a way to detect the top-level printers too
+ # the problem is that some of them collide and the order *is*
+ # important, but sets and dicts don't preserve order.
+ # Particularly, 'PointerPrinter' must come after 'FastPrinters'.
obj = gdb.current_objfile()
if obj is None:
@@ -53,9 +86,12 @@ def finish():
else:
filename = obj.filename
obj.pretty_printers.append(fp)
+ n = len(obj.pretty_printers)
obj.pretty_printers.append(ep)
- print('Added %d+1 custom printers for %s'
- % (len(fp.printers), filename))
+ obj.pretty_printers.append(ptrp)
+ n = len(obj.pretty_printers) - n
+ print('Added %d+%d custom printers for %s'
+ % (len(fp.printers), n, filename))
class EnumPrinter(object):
__slots__ = ('_value')
@@ -82,6 +118,49 @@ class EnumPrinter(object):
scope = get_basic_type(v.type).tag
return '%s::%s' % (scope, name)
+class PointerPrinter(object):
+ __slots__ = ('_value')
+ name = 'any-symbol-pointer'
+ enabled = True
+
+ def __new__(cls, v):
+ type = get_basic_type(v.type)
+ if type.code != gdb.TYPE_CODE_PTR:
+ return None
+ return object.__new__(cls)
+
+ def __init__(self, v):
+ self._value = v
+
+ def to_string(self):
+ v = self._value
+ 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 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
+ return s
+
+
class FastPrinters(object):
''' printer dispatch the way gdb *should* have done it
'''
diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp
index 0d70b36..7739966 100644
--- a/src/map/atcommand.cpp
+++ b/src/map/atcommand.cpp
@@ -24,8 +24,6 @@
#include <algorithm>
-#include "../conf/version.hpp"
-
#include "../compat/nullpo.hpp"
#include "../compat/fun.hpp"
@@ -39,31 +37,40 @@
#include "../generic/random.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
+#include "../io/extract.hpp"
#include "../io/read.hpp"
#include "../io/write.hpp"
#include "../net/socket.hpp"
#include "../net/timer.hpp"
+#include "../net/timestamp-utils.hpp"
#include "../mmo/config_parse.hpp"
-#include "../mmo/core.hpp"
-#include "../mmo/extract.hpp"
+#include "../mmo/cxxstdio_enums.hpp"
#include "../mmo/extract_enums.hpp"
#include "../mmo/human_time_diff.hpp"
#include "../mmo/ids.hpp"
-#include "../mmo/mmo.hpp"
-#include "../mmo/utils.hpp"
#include "../mmo/version.hpp"
+#include "../high/core.hpp"
+#include "../high/extract_mmo.hpp"
+#include "../high/mmo.hpp"
+#include "../high/utils.hpp"
+
+#include "../ast/npc.hpp"
+
#include "battle.hpp"
+#include "battle_conf.hpp"
#include "chrif.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "intif.hpp"
#include "itemdb.hpp"
#include "map.hpp"
+#include "map_conf.hpp"
#include "mob.hpp"
#include "npc.hpp"
+#include "npc-parse.hpp"
#include "party.hpp"
#include "pc.hpp"
#include "skill.hpp"
@@ -76,6 +83,8 @@
namespace tmwa
{
+namespace map
+{
enum class ATCE
{
OKAY,
@@ -105,7 +114,7 @@ Map<XString, AtCommandInfo> atcommand_info;
static
-AtCommandInfo *atcommand(XString cmd);
+Option<Borrowed<AtCommandInfo>> atcommand(XString cmd);
// These @commands are used within other @commands.
static
@@ -203,9 +212,7 @@ void log_atcommand(dumb_ptr<map_session_data> sd, ZString cmd)
return;
timestamp_seconds_buffer tmpstr;
stamp_time(tmpstr);
- MapName map = (sd->bl_m
- ? sd->bl_m->name_
- : stringish<MapName>("undefined.gat"_s));
+ MapName map = (sd->bl_m->name_);
FPRINTF(*fp, "[%s] %s(%d,%d) %s(%d) : %s\n"_fmt,
tmpstr,
map, sd->bl_x, sd->bl_y,
@@ -213,11 +220,9 @@ void log_atcommand(dumb_ptr<map_session_data> sd, ZString cmd)
cmd);
}
-AString gm_log;
-
io::AppendFile *get_gm_log()
{
- if (!gm_log)
+ if (!map_conf.gm_log)
return nullptr;
struct tm ctime = TimeT::now();
@@ -233,7 +238,7 @@ io::AppendFile *get_gm_log()
last_logfile_nr = logfile_nr;
AString fullname = STRPRINTF("%s.%04d-%02d"_fmt,
- gm_log, year, month);
+ map_conf.gm_log, year, month);
if (gm_logfile)
gm_logfile.reset();
@@ -243,7 +248,7 @@ io::AppendFile *get_gm_log()
if (!gm_logfile)
{
perror("GM log file");
- gm_log = AString();
+ map_conf.gm_log = AString();
}
return gm_logfile.get();
}
@@ -260,8 +265,6 @@ bool is_atcommand(Session *s, dumb_ptr<map_session_data> sd,
ZString arg;
asplit(message, &command, &arg);
- AtCommandInfo *info = atcommand(command);
-
if (!gmlvl)
gmlvl = pc_isGM(sd);
if (battle_config.atcommand_gm_only != 0 && !gmlvl)
@@ -271,14 +274,16 @@ bool is_atcommand(Session *s, dumb_ptr<map_session_data> sd,
clif_displaymessage(s, output);
return true;
}
- if (!info)
+
+ Option<P<AtCommandInfo>> info_ = atcommand(command);
+ P<AtCommandInfo> info = TRY_UNWRAP(info_,
{
AString output = STRPRINTF("GM command not found: %s"_fmt,
AString(command));
clif_displaymessage(s, output);
return true;
// don't show in chat
- }
+ });
if (!(gmlvl.satisfies(info->level)))
{
AString output = STRPRINTF("GM command is level %d, but you are level %d: %s"_fmt,
@@ -320,15 +325,15 @@ bool is_atcommand(Session *s, dumb_ptr<map_session_data> sd,
}
}
-AtCommandInfo *atcommand(XString cmd)
+Option<Borrowed<AtCommandInfo>> atcommand(XString cmd)
{
if (cmd.startswith('@'))
{
XString command = cmd.xslice_t(1);
- AtCommandInfo *it = atcommand_info.search(command);
+ Option<P<AtCommandInfo>> it = atcommand_info.search(command);
return it;
}
- return nullptr;
+ return None;
}
static
@@ -344,51 +349,42 @@ void atkillmonster_sub(dumb_ptr<block_list> bl, int flag)
}
static
-AtCommandInfo *get_atcommandinfo_byname(XString name)
+Option<Borrowed<AtCommandInfo>> get_atcommandinfo_byname(XString name)
{
return atcommand_info.search(name);
}
-bool atcommand_config_read(ZString cfgName)
+static
+bool atcommand_config(io::Spanned<XString> w1, io::Spanned<ZString> w2)
{
- io::ReadFile in(cfgName);
- if (!in.is_open())
- {
- PRINTF("At commands configuration file not found: %s\n"_fmt, cfgName);
- return false;
- }
-
bool rv = true;
- AString line;
- while (in.getline(line))
{
- if (is_comment(line))
- continue;
- XString w1;
- ZString w2;
- if (!config_split(line, &w1, &w2))
+ Option<P<AtCommandInfo>> p_ = get_atcommandinfo_byname(w1.data);
+ OMATCH_BEGIN (p_)
{
- PRINTF("Bad config line: %s\n"_fmt, line);
- rv = false;
- continue;
- }
- AtCommandInfo *p = get_atcommandinfo_byname(w1);
- if (p != nullptr)
- {
- p->level = GmLevel::from(static_cast<uint32_t>(atoi(w2.c_str())));
- }
- else if (w1 == "import"_s)
- rv &= atcommand_config_read(w2);
- else
- {
- PRINTF("%s: bad line: %s\n"_fmt, cfgName, line);
- rv = false;
+ OMATCH_CASE_SOME (p)
+ {
+ p->level = GmLevel::from(static_cast<uint32_t>(atoi(w2.data.c_str())));
+ }
+ OMATCH_CASE_NONE ()
+ {
+ {
+ w1.span.error("Unknown @command for permission level config."_s);
+ rv = false;
+ }
+ }
}
+ OMATCH_END ();
}
return rv;
}
+bool atcommand_config_read(ZString cfgName)
+{
+ return load_config_file(cfgName, atcommand_config);
+}
+
/// @ command processing functions
static
@@ -421,9 +417,7 @@ ATCE atcommand_help(Session *s, dumb_ptr<map_session_data>,
if (message.startswith('@'))
{
ZString cmd = message.xslice_t(1);
- const AtCommandInfo *info = atcommand_info.search(cmd);
- if (!info)
- return ATCE::EXIST;
+ P<AtCommandInfo> info = TRY_UNWRAP(atcommand_info.search(cmd), return ATCE::EXIST);
clif_displaymessage(s, STRPRINTF("Usage: @%s %s"_fmt, cmd, info->args));
clif_displaymessage(s, info->help);
return ATCE::OKAY;
@@ -534,16 +528,16 @@ ATCE atcommand_charwarp(Session *s, dumb_ptr<map_session_data> sd,
// you can rura+ only lower or same GM level
if (x > 0 && x < 800 && y > 0 && y < 800)
{
- map_local *m = map_mapname2mapid(map_name);
- if (m != nullptr && m->flag.get(MapFlag::NOWARPTO)
- && !pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))
+ Option<P<map_local>> m = map_mapname2mapid(map_name);
+ if (m.map([](P<map_local> m_){ return m_->flag.get(MapFlag::NOWARPTO); }).copy_or(false)
+ && !pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))
{
clif_displaymessage(s,
"You are not authorised to warp someone to this map."_s);
return ATCE::PERM;
}
- if (pl_sd->bl_m && pl_sd->bl_m->flag.get(MapFlag::NOWARP)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (pl_sd->bl_m->flag.get(MapFlag::NOWARP)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp this player from its actual map."_s);
@@ -603,16 +597,16 @@ ATCE atcommand_warp(Session *s, dumb_ptr<map_session_data> sd,
if (x > 0 && x < 800 && y > 0 && y < 800)
{
- map_local *m = map_mapname2mapid(map_name);
- if (m != nullptr && m->flag.get(MapFlag::NOWARPTO)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ Option<P<map_local>> m = map_mapname2mapid(map_name);
+ if (m.map([](P<map_local> m_){ return m_->flag.get(MapFlag::NOWARPTO); }).copy_or(false)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp you to this map."_s);
return ATCE::PERM;
}
- if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARP)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (sd->bl_m->flag.get(MapFlag::NOWARP)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp you from your actual map."_s);
@@ -645,7 +639,7 @@ ATCE atcommand_where(Session *s, dumb_ptr<map_session_data> sd,
dumb_ptr<map_session_data> pl_sd = character.to__actual() ? map_nick2sd(character) : sd;
if (pl_sd != nullptr &&
!((battle_config.hide_GM_session
- || bool(pl_sd->status.option & Option::HIDE))
+ || bool(pl_sd->status.option & Opt0::HIDE))
&& !(pc_isGM(sd).detects(pc_isGM(pl_sd)))))
{
// you can look only lower or same level
@@ -679,15 +673,15 @@ ATCE atcommand_goto(Session *s, dumb_ptr<map_session_data> sd,
dumb_ptr<map_session_data> pl_sd = map_nick2sd(character);
if (pl_sd != nullptr)
{
- if (pl_sd->bl_m && pl_sd->bl_m->flag.get(MapFlag::NOWARPTO)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (pl_sd->bl_m->flag.get(MapFlag::NOWARPTO)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp you to the map of this player."_s);
return ATCE::PERM;
}
- if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARP)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (sd->bl_m->flag.get(MapFlag::NOWARP)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp you from your actual map."_s);
@@ -707,6 +701,69 @@ ATCE atcommand_goto(Session *s, dumb_ptr<map_session_data> sd,
}
static
+ATCE atcommand_npc(Session *s, dumb_ptr<map_session_data> sd,
+ ZString message)
+{
+ NpcName npc;
+
+ if (!asplit(message, &npc))
+ {
+ clif_displaymessage(s,
+ "Please, enter a npc name (usage: @npc/@warptonpc/@gotonpc <npc>)."_s);
+ return ATCE::USAGE;
+ }
+
+ dumb_ptr<npc_data> nd = npc_name2id(npc);
+ if (nd != nullptr)
+ {
+ if (nd->bl_m->flag.get(MapFlag::NOWARPTO)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
+ {
+ clif_displaymessage(s,
+ "You are not authorised to warp you to the map of this npc."_s);
+ return ATCE::PERM;
+ }
+ if (sd->bl_m->flag.get(MapFlag::NOWARP)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
+ {
+ clif_displaymessage(s,
+ "You are not authorised to warp you from your actual map."_s);
+ return ATCE::PERM;
+ }
+
+ int x = nd->bl_x, y = nd->bl_y, x0 = (x >= 5)? (x - 5): 0, j = 0,
+ y0 = (y >= 5)? (y - 5): 0, x1 = (x + 5), y1 = (y + 5), max;
+ max = (y1 - y0 + 1) * (x1 - x0 + 1) * 3;
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(nd->bl_m->name_), return ATCE::OKAY);
+ if (max > 1000)
+ max = 1000;
+ if (bool(map_getcell(m, x, y) & MapCell::UNWALKABLE)){
+ do
+ {
+ x = random_::in(x0, x1);
+ y = random_::in(y0, y1);
+ }
+ while (bool(map_getcell(m, x, y) & MapCell::UNWALKABLE)
+ && (++j) < max);
+ if (j >= max)
+ {
+ return ATCE::OKAY; // Since reference of the place which boils first went wrong, it stops.
+ }
+ }
+ pc_setpos(sd, nd->bl_m->name_, x, y, BeingRemoveWhy::WARPED);
+ AString output = STRPRINTF("Jump to %s"_fmt, npc);
+ clif_displaymessage(s, output);
+ }
+ else
+ {
+ clif_displaymessage(s, "Npc not found."_s);
+ return ATCE::EXIST;
+ }
+
+ return ATCE::OKAY;
+}
+
+static
ATCE atcommand_jump(Session *s, dumb_ptr<map_session_data> sd,
ZString message)
{
@@ -720,15 +777,15 @@ ATCE atcommand_jump(Session *s, dumb_ptr<map_session_data> sd,
y = random_::in(1, 399);
if (x > 0 && x < 800 && y > 0 && y < 800)
{
- if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARPTO)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (sd->bl_m->flag.get(MapFlag::NOWARPTO)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp you to your actual map."_s);
return ATCE::PERM;
}
- if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARP)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (sd->bl_m->flag.get(MapFlag::NOWARP)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp you from your actual map."_s);
@@ -768,7 +825,7 @@ ATCE atcommand_who(Session *s, dumb_ptr<map_session_data> sd,
GmLevel pl_gm_level = pc_isGM(pl_sd);
if (!
((battle_config.hide_GM_session
- || bool(pl_sd->status.option & Option::HIDE))
+ || bool(pl_sd->status.option & Opt0::HIDE))
&& !(gm_level.detects(pl_gm_level))))
{
// you can look only lower or same level
@@ -812,7 +869,6 @@ ATCE atcommand_whogroup(Session *s, dumb_ptr<map_session_data> sd,
ZString message)
{
int count;
- PartyPair p;
VString<23> match_text = message;
match_text = match_text.to_lower();
@@ -830,7 +886,7 @@ ATCE atcommand_whogroup(Session *s, dumb_ptr<map_session_data> sd,
GmLevel pl_gm_level = pc_isGM(pl_sd);
if (!
((battle_config.hide_GM_session
- || bool(pl_sd->status.option & Option::HIDE))
+ || bool(pl_sd->status.option & Opt0::HIDE))
&& (!(gm_level.detects(pl_gm_level)))))
{
// you can look only lower or same level
@@ -838,8 +894,8 @@ ATCE atcommand_whogroup(Session *s, dumb_ptr<map_session_data> sd,
if (player_name.contains_seq(match_text))
{
// search with no case sensitive
- p = party_search(pl_sd->status.party_id);
- PartyName temp0 = p ? p->name : stringish<PartyName>("None"_s);
+ Option<PartyPair> p_ = party_search(pl_sd->status.party_id);
+ PartyName temp0 = p_.pmd_pget(&PartyMost::name).move_or(stringish<PartyName>("None"_s));
AString output;
if (pl_gm_level)
output = STRPRINTF(
@@ -870,15 +926,14 @@ ATCE atcommand_whomap(Session *s, dumb_ptr<map_session_data> sd,
ZString message)
{
int count;
- map_local *map_id;
- {
+ Borrowed<map_local> map_id =
+ ({
MapName map_name;
extract(message, &map_name);
- map_id = map_mapname2mapid(map_name);
- if (map_id == nullptr)
- map_id = sd->bl_m;
- }
+
+ map_mapname2mapid(map_name).copy_or(sd->bl_m);
+ });
count = 0;
GmLevel gm_level = pc_isGM(sd);
@@ -893,7 +948,7 @@ ATCE atcommand_whomap(Session *s, dumb_ptr<map_session_data> sd,
GmLevel pl_gm_level = pc_isGM(pl_sd);
if (!
((battle_config.hide_GM_session
- || bool(pl_sd->status.option & Option::HIDE))
+ || bool(pl_sd->status.option & Opt0::HIDE))
&& (!(gm_level.detects(pl_gm_level)))))
{
// you can look only lower or same level
@@ -929,16 +984,14 @@ ATCE atcommand_whomapgroup(Session *s, dumb_ptr<map_session_data> sd,
ZString message)
{
int count;
- PartyPair p;
- map_local *map_id;
- {
+ P<map_local> map_id =
+ ({
MapName map_name;
extract(message, &map_name);
- map_id = map_mapname2mapid(map_name);
- if (map_id == nullptr)
- map_id = sd->bl_m;
- }
+
+ map_mapname2mapid(map_name).copy_or(sd->bl_m);
+ });
count = 0;
GmLevel gm_level = pc_isGM(sd);
@@ -953,14 +1006,14 @@ ATCE atcommand_whomapgroup(Session *s, dumb_ptr<map_session_data> sd,
GmLevel pl_gm_level = pc_isGM(pl_sd);
if (!
((battle_config.hide_GM_session
- || bool(pl_sd->status.option & Option::HIDE))
+ || bool(pl_sd->status.option & Opt0::HIDE))
&& (!(gm_level.detects(pl_gm_level)))))
{
// you can look only lower or same level
if (pl_sd->bl_m == map_id)
{
- p = party_search(pl_sd->status.party_id);
- PartyName temp0 = p ? p->name : stringish<PartyName>("None"_s);
+ Option<PartyPair> p_ = party_search(pl_sd->status.party_id);
+ PartyName temp0 = p_.pmd_pget(&PartyMost::name).copy_or(stringish<PartyName>("None"_s));
AString output;
if (pl_gm_level)
output = STRPRINTF("Name: %s (GM:%d) | Party: '%s'"_fmt,
@@ -994,7 +1047,6 @@ ATCE atcommand_whogm(Session *s, dumb_ptr<map_session_data> sd,
ZString message)
{
int count;
- PartyPair p;
VString<23> match_text = message;
match_text = match_text.to_lower();
@@ -1014,7 +1066,7 @@ ATCE atcommand_whogm(Session *s, dumb_ptr<map_session_data> sd,
{
if (!
((battle_config.hide_GM_session
- || bool(pl_sd->status.option & Option::HIDE))
+ || bool(pl_sd->status.option & Opt0::HIDE))
&& (!(gm_level.detects(pl_gm_level)))))
{
// you can look only lower or same level
@@ -1034,8 +1086,8 @@ ATCE atcommand_whogm(Session *s, dumb_ptr<map_session_data> sd,
"Novice/Human"_s,
pl_sd->status.job_level);
clif_displaymessage(s, output);
- p = party_search(pl_sd->status.party_id);
- PartyName temp0 = p ? p->name : stringish<PartyName>("None"_s);
+ Option<PartyPair> p_ = party_search(pl_sd->status.party_id);
+ PartyName temp0 = p_.pmd_pget(&PartyMost::name).copy_or(stringish<PartyName>("None"_s));
output = STRPRINTF(
" Party: '%s'"_fmt,
temp0);
@@ -1076,16 +1128,16 @@ static
ATCE atcommand_load(Session *s, dumb_ptr<map_session_data> sd,
ZString)
{
- map_local *m = map_mapname2mapid(sd->status.save_point.map_);
- if (m != nullptr && m->flag.get(MapFlag::NOWARPTO)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ Option<P<map_local>> m = map_mapname2mapid(sd->status.save_point.map_);
+ if (m.map([](P<map_local> m_){ return m_->flag.get(MapFlag::NOWARPTO); }).copy_or(false)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp you to your save map."_s);
return ATCE::PERM;
}
- if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARP)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (sd->bl_m->flag.get(MapFlag::NOWARP)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp you from your actual map."_s);
@@ -1148,16 +1200,13 @@ static
ATCE atcommand_storage(Session *s, dumb_ptr<map_session_data> sd,
ZString)
{
- Storage *stor;
-
if (sd->state.storage_open)
{
clif_displaymessage(s, "msg_table[250]"_s);
return ATCE::EXIST;
}
- if ((stor = account2storage2(sd->status_key.account_id)) != nullptr
- && stor->storage_status == 1)
+ if (account2storage2(sd->status_key.account_id).pmd_pget(&Storage::storage_status).copy_or(0) == 1)
{
clif_displaymessage(s, "msg_table[250]"_s);
return ATCE::EXIST;
@@ -1174,7 +1223,7 @@ ATCE atcommand_option(Session *s, dumb_ptr<map_session_data> sd,
{
Opt1 param1 = Opt1::ZERO;
Opt2 param2 = Opt2::ZERO;
- Option param3 = Option::ZERO;
+ Opt0 param3 = Opt0::ZERO;
if (!extract(message, record<',', 1>(&param1, &param2, &param3)))
return ATCE::USAGE;
@@ -1194,14 +1243,14 @@ static
ATCE atcommand_hide(Session *s, dumb_ptr<map_session_data> sd,
ZString)
{
- if (bool(sd->status.option & Option::HIDE))
+ if (bool(sd->status.option & Opt0::HIDE))
{
- sd->status.option &= ~Option::HIDE;
+ sd->status.option &= ~Opt0::HIDE;
clif_displaymessage(s, "Invisible: Off."_s);
}
else
{
- sd->status.option |= Option::HIDE;
+ sd->status.option |= Opt0::HIDE;
clif_displaymessage(s, "Invisible: On."_s);
}
clif_changeoption(sd);
@@ -1259,8 +1308,8 @@ ATCE atcommand_alive(Session *s, dumb_ptr<map_session_data> sd,
sd->status.hp = sd->status.max_hp;
sd->status.sp = sd->status.max_sp;
pc_setstand(sd);
- if (static_cast<interval_t>(battle_config.player_invincible_time) > interval_t::zero())
- pc_setinvincibletimer(sd, static_cast<interval_t>(battle_config.player_invincible_time));
+ if (battle_config.player_invincible_time > interval_t::zero())
+ pc_setinvincibletimer(sd, battle_config.player_invincible_time);
clif_updatestatus(sd, SP::HP);
clif_updatestatus(sd, SP::SP);
clif_resurrection(sd, 1);
@@ -1312,7 +1361,7 @@ ATCE atcommand_heal(Session *s, dumb_ptr<map_session_data> sd,
if (hp < 0)
// display like damage
- clif_damage(sd, sd, gettick(), interval_t::zero(), interval_t::zero(), -hp, 0, DamageType::RETURNED, 0);
+ clif_damage(sd, sd, gettick(), interval_t::zero(), interval_t::zero(), -hp, 0, DamageType::RETURNED);
if (hp != 0 || sp != 0)
{
@@ -1332,13 +1381,27 @@ ATCE atcommand_heal(Session *s, dumb_ptr<map_session_data> sd,
}
static
+Option<P<struct item_data>> extract_item_opt(XString item_name)
+{
+ Option<P<struct item_data>> item_data = itemdb_searchname(item_name);
+ if (item_data.is_some())
+ return item_data;
+
+ ItemNameId item_id;
+ if (extract(item_name, &item_id))
+ {
+ item_data = itemdb_exists(item_id);
+ return item_data;
+ }
+ return None;
+}
+
+static
ATCE atcommand_item(Session *s, dumb_ptr<map_session_data> sd,
ZString message)
{
XString item_name;
int number = 0;
- ItemNameId item_id;
- struct item_data *item_data = nullptr;
int get_count, i;
if (!extract(message, record<' ', 1>(&item_name, &number)))
@@ -1351,14 +1414,10 @@ ATCE atcommand_item(Session *s, dumb_ptr<map_session_data> sd,
if (number <= 0)
number = 1;
- if ((item_data = itemdb_searchname(item_name)) != nullptr)
- item_id = item_data->nameid;
- else if (extract(item_name, &item_id) && (item_data = itemdb_exists(item_id)) != nullptr)
- item_id = item_data->nameid;
- else
- return ATCE::EXIST;
+ P<struct item_data> item_data = TRY_UNWRAP(extract_item_opt(item_name), return ATCE::EXIST);
+ ItemNameId item_id = item_data->nameid;
+ assert (item_id);
- if (item_id)
{
get_count = number;
if (item_data->type == ItemType::WEAPON
@@ -1379,11 +1438,6 @@ ATCE atcommand_item(Session *s, dumb_ptr<map_session_data> sd,
}
clif_displaymessage(s, "Item created."_s);
}
- else
- {
- clif_displaymessage(s, "Invalid item ID or name."_s);
- return ATCE::EXIST;
- }
return ATCE::OKAY;
}
@@ -1537,25 +1591,6 @@ ATCE atcommand_joblevelup(Session *s, dumb_ptr<map_session_data> sd,
}
static
-ATCE atcommand_gm(Session *s, dumb_ptr<map_session_data> sd,
- ZString message)
-{
- if (!message)
- return ATCE::USAGE;
-
- if (pc_isGM(sd))
- {
- // a GM can not use this function. only a normal player (become gm is not for gm!)
- clif_displaymessage(s, "You already have some GM powers."_s);
- return ATCE::PERM;
- }
- else
- chrif_changegm(sd->status_key.account_id, message);
-
- return ATCE::OKAY;
-}
-
-static
ATCE atcommand_pvpoff(Session *s, dumb_ptr<map_session_data> sd,
ZString)
{
@@ -1595,6 +1630,25 @@ ATCE atcommand_pvpoff(Session *s, dumb_ptr<map_session_data> sd,
}
static
+ATCE atcommand_exprate(Session *s, dumb_ptr<map_session_data>,
+ ZString message)
+{
+ int rate;
+
+ if (!extract(message, &rate) || !rate)
+ {
+ clif_displaymessage(s,
+ "Please, enter a rate adjustement (usage: @exprate <percent>)."_s);
+ return ATCE::USAGE;
+ }
+ battle_config.base_exp_rate = rate;
+ battle_config.job_exp_rate = rate;
+ AString output = STRPRINTF("All Xp at %d percent"_fmt, rate);
+ clif_displaymessage(s, output);
+ return ATCE::OKAY;
+}
+
+static
ATCE atcommand_pvpon(Session *s, dumb_ptr<map_session_data> sd,
ZString)
{
@@ -1621,7 +1675,6 @@ ATCE atcommand_pvpon(Session *s, dumb_ptr<map_session_data> sd,
pl_sd->pvp_timer = Timer(gettick() + 200_ms,
std::bind(pc_calc_pvprank_timer, ph::_1, ph::_2, pl_sd->bl_id));
pl_sd->pvp_rank = 0;
- pl_sd->pvp_lastusers = 0;
pl_sd->pvp_point = 5;
}
}
@@ -1809,14 +1862,13 @@ static
void atcommand_killmonster_sub(Session *s, dumb_ptr<map_session_data> sd,
ZString message, const int drop)
{
- map_local *map_id;
- {
+ P<map_local> map_id =
+ ({
MapName map_name;
extract(message, &map_name);
- map_id = map_mapname2mapid(map_name);
- if (map_id == nullptr)
- map_id = sd->bl_m;
- }
+
+ map_mapname2mapid(map_name).copy_or(sd->bl_m);
+ });
map_foreachinarea(std::bind(atkillmonster_sub, ph::_1, drop),
map_id,
@@ -2080,15 +2132,15 @@ ATCE atcommand_recall(Session *s, dumb_ptr<map_session_data> sd,
if (pc_isGM(sd).overwhelms(pc_isGM(pl_sd)))
{
// you can recall only lower or same level
- if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARPTO)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (sd->bl_m->flag.get(MapFlag::NOWARPTO)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp somenone to your actual map."_s);
return ATCE::PERM;
}
- if (pl_sd->bl_m && pl_sd->bl_m->flag.get(MapFlag::NOWARP)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (pl_sd->bl_m->flag.get(MapFlag::NOWARP)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp this player from its actual map."_s);
@@ -2127,8 +2179,8 @@ ATCE atcommand_revive(Session *s, dumb_ptr<map_session_data> sd,
{
pl_sd->status.hp = pl_sd->status.max_hp;
pc_setstand(pl_sd);
- if (static_cast<interval_t>(battle_config.player_invincible_time) > interval_t::zero())
- pc_setinvincibletimer(sd, static_cast<interval_t>(battle_config.player_invincible_time));
+ if (battle_config.player_invincible_time > interval_t::zero())
+ pc_setinvincibletimer(sd, battle_config.player_invincible_time);
clif_updatestatus(pl_sd, SP::HP);
clif_updatestatus(pl_sd, SP::SP);
clif_resurrection(pl_sd, 1);
@@ -2257,7 +2309,7 @@ ATCE atcommand_character_option(Session *s, dumb_ptr<map_session_data> sd,
{
Opt1 opt1;
Opt2 opt2;
- Option opt3;
+ Opt0 opt3;
CharName character;
if (!asplit(message, &opt1, &opt2, &opt3, &character))
return ATCE::USAGE;
@@ -2403,16 +2455,15 @@ ATCE atcommand_character_save(Session *s, dumb_ptr<map_session_data> sd,
if (pc_isGM(sd).overwhelms(pc_isGM(pl_sd)))
{
// you can change save point only to lower or same gm level
- map_local *m = map_mapname2mapid(map_name);
- if (m == nullptr)
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(map_name),
{
clif_displaymessage(s, "Map not found."_s);
return ATCE::EXIST;
- }
- else
+ });
+
{
- if (m != nullptr && m->flag.get(MapFlag::NOWARPTO)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (m->flag.get(MapFlag::NOWARPTO)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to set this map as a save map."_s);
@@ -2976,7 +3027,6 @@ ATCE atcommand_idsearch(Session *s, dumb_ptr<map_session_data>,
{
ItemName item_name;
int match;
- struct item_data *item;
if (!extract(message, &item_name) || !item_name)
return ATCE::USAGE;
@@ -2986,8 +3036,8 @@ ATCE atcommand_idsearch(Session *s, dumb_ptr<map_session_data>,
match = 0;
for (ItemNameId i = wrap<ItemNameId>(0); i < wrap<ItemNameId>(-1); i = next(i))
{
- if ((item = itemdb_exists(i)) != nullptr
- && item->jname.contains_seq(item_name))
+ P<struct item_data> item = TRY_UNWRAP(itemdb_exists(i), continue);
+ if (item->jname.contains_seq(item_name))
{
match++;
output = STRPRINTF("%s: %d"_fmt, item->jname, item->nameid);
@@ -3350,8 +3400,8 @@ ATCE atcommand_recallall(Session *s, dumb_ptr<map_session_data> sd,
{
int count;
- if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARPTO)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (sd->bl_m->flag.get(MapFlag::NOWARPTO)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp somenone to your actual map."_s);
@@ -3371,8 +3421,8 @@ ATCE atcommand_recallall(Session *s, dumb_ptr<map_session_data> sd,
&& pc_isGM(sd).overwhelms(pc_isGM(pl_sd)))
{
// you can recall only lower or same level
- if (pl_sd->bl_m && pl_sd->bl_m->flag.get(MapFlag::NOWARP)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (pl_sd->bl_m->flag.get(MapFlag::NOWARP)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
count++;
else
pc_setpos(pl_sd, sd->mapname_, sd->bl_x, sd->bl_y, BeingRemoveWhy::QUIT);
@@ -3396,57 +3446,62 @@ ATCE atcommand_partyrecall(Session *s, dumb_ptr<map_session_data> sd,
ZString message)
{
PartyName party_name;
- PartyPair p;
int count;
if (!extract(message, &party_name) || !party_name)
return ATCE::USAGE;
- if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARPTO)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (sd->bl_m->flag.get(MapFlag::NOWARPTO)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp somenone to your actual map."_s);
return ATCE::PERM;
}
- if ((p = party_searchname(party_name)) ||
- // name first to avoid error when name begin with a number
- (p = party_search(wrap<PartyId>(static_cast<uint32_t>(atoi(message.c_str()))))))
+ // name first to avoid error when name begin with a number
+ Option<PartyPair> p_ = party_searchname(party_name);
+ if (p_.is_none())
+ p_ = party_search(wrap<PartyId>(static_cast<uint32_t>(atoi(message.c_str()))));
+ OMATCH_BEGIN (p_)
{
- count = 0;
- for (io::FD i : iter_fds())
+ OMATCH_CASE_SOME (p)
{
- Session *s2 = get_session(i);
- if (!s2)
- continue;
- dumb_ptr<map_session_data> pl_sd = dumb_ptr<map_session_data>(static_cast<map_session_data *>(s2->session_data.get()));
- if (pl_sd && pl_sd->state.auth
- && sd->status_key.account_id != pl_sd->status_key.account_id
- && pl_sd->status.party_id == p.party_id)
+ count = 0;
+ for (io::FD i : iter_fds())
{
- if (pl_sd->bl_m && pl_sd->bl_m->flag.get(MapFlag::NOWARP)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
- count++;
- else
- pc_setpos(pl_sd, sd->mapname_, sd->bl_x, sd->bl_y, BeingRemoveWhy::QUIT);
+ Session *s2 = get_session(i);
+ if (!s2)
+ continue;
+ dumb_ptr<map_session_data> pl_sd = dumb_ptr<map_session_data>(static_cast<map_session_data *>(s2->session_data.get()));
+ if (pl_sd && pl_sd->state.auth
+ && sd->status_key.account_id != pl_sd->status_key.account_id
+ && pl_sd->status.party_id == p.party_id)
+ {
+ if (pl_sd->bl_m->flag.get(MapFlag::NOWARP)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
+ count++;
+ else
+ pc_setpos(pl_sd, sd->mapname_, sd->bl_x, sd->bl_y, BeingRemoveWhy::QUIT);
+ }
+ }
+ AString output = STRPRINTF("All online characters of the %s party are near you."_fmt, p->name);
+ clif_displaymessage(s, output);
+ if (count)
+ {
+ output = STRPRINTF(
+ "Because you are not authorised to warp from some maps, %d player(s) have not been recalled."_fmt,
+ count);
+ clif_displaymessage(s, output);
}
}
- AString output = STRPRINTF("All online characters of the %s party are near you."_fmt, p->name);
- clif_displaymessage(s, output);
- if (count)
+ OMATCH_CASE_NONE ()
{
- output = STRPRINTF(
- "Because you are not authorised to warp from some maps, %d player(s) have not been recalled."_fmt,
- count);
- clif_displaymessage(s, output);
+ clif_displaymessage(s, "Incorrect name or ID, or no one from the party is online."_s);
+ return ATCE::EXIST;
}
}
- else
- {
- clif_displaymessage(s, "Incorrect name or ID, or no one from the party is online."_s);
- return ATCE::EXIST;
- }
+ OMATCH_END ();
return ATCE::OKAY;
}
@@ -3468,9 +3523,7 @@ ATCE atcommand_mapinfo(Session *s, dumb_ptr<map_session_data> sd,
if (!map_name)
map_name = sd->mapname_;
- map_local *m_id = map_mapname2mapid(map_name);
- if (m_id != nullptr)
- return ATCE::EXIST;
+ P<map_local> m_id = TRY_UNWRAP(map_mapname2mapid(map_name), return ATCE::EXIST);
clif_displaymessage(s, "------ Map Info ------"_s);
AString output = STRPRINTF("Map Name: %s"_fmt, map_name);
@@ -3592,29 +3645,34 @@ ATCE atcommand_partyspy(Session *s, dumb_ptr<map_session_data> sd,
if (!extract(message, &party_name))
return ATCE::USAGE;
- PartyPair p;
- if ((p = party_searchname(party_name)) ||
- // name first to avoid error when name begin with a number
- (p = party_search(wrap<PartyId>(static_cast<uint32_t>(atoi(message.c_str()))))))
+ // name first to avoid error when name begin with a number
+ Option<PartyPair> p_ = party_searchname(party_name);
+ if (p_.is_none())
+ p_ = party_search(wrap<PartyId>(static_cast<uint32_t>(atoi(message.c_str()))));
+ OMATCH_BEGIN (p_)
{
- if (sd->partyspy == p.party_id)
+ OMATCH_CASE_SOME (p)
{
- sd->partyspy = PartyId();
- AString output = STRPRINTF("No longer spying on the %s party."_fmt, p->name);
- clif_displaymessage(s, output);
+ if (sd->partyspy == p.party_id)
+ {
+ sd->partyspy = PartyId();
+ AString output = STRPRINTF("No longer spying on the %s party."_fmt, p->name);
+ clif_displaymessage(s, output);
+ }
+ else
+ {
+ sd->partyspy = p.party_id;
+ AString output = STRPRINTF("Spying on the %s party."_fmt, p->name);
+ clif_displaymessage(s, output);
+ }
}
- else
+ OMATCH_CASE_NONE ()
{
- sd->partyspy = p.party_id;
- AString output = STRPRINTF("Spying on the %s party."_fmt, p->name);
- clif_displaymessage(s, output);
+ clif_displaymessage(s, "Incorrect name or ID, or no one from the party is online."_s);
+ return ATCE::EXIST;
}
}
- else
- {
- clif_displaymessage(s, "Incorrect name or ID, or no one from the party is online."_s);
- return ATCE::EXIST;
- }
+ OMATCH_END ();
return ATCE::OKAY;
}
@@ -3684,19 +3742,15 @@ ATCE atcommand_chardelitem(Session *s, dumb_ptr<map_session_data> sd,
CharName character;
XString item_name;
int i, number = 0;
- ItemNameId item_id;
int count;
- struct item_data *item_data;
if (!asplit(message, &item_name, &number, &character) || number < 1)
return ATCE::USAGE;
- if ((item_data = itemdb_searchname(item_name)) != nullptr)
- item_id = item_data->nameid;
- else if (extract(item_name, &item_id) && (item_data = itemdb_exists(item_id)) != nullptr)
- item_id = item_data->nameid;
+ P<struct item_data> item_data = TRY_UNWRAP(extract_item_opt(item_name), return ATCE::EXIST);
+ ItemNameId item_id = item_data->nameid;
+ assert (item_id);
- if (item_id)
{
dumb_ptr<map_session_data> pl_sd = map_nick2sd(character);
if (pl_sd != nullptr)
@@ -3744,11 +3798,6 @@ ATCE atcommand_chardelitem(Session *s, dumb_ptr<map_session_data> sd,
return ATCE::EXIST;
}
}
- else
- {
- clif_displaymessage(s, "Invalid item ID or name."_s);
- return ATCE::RANGE;
- }
return ATCE::OKAY;
}
@@ -3855,7 +3904,6 @@ static
ATCE atcommand_character_item_list(Session *s, dumb_ptr<map_session_data> sd,
ZString message)
{
- struct item_data *item_data = nullptr;
int count, counter;
CharName character;
@@ -3872,10 +3920,10 @@ ATCE atcommand_character_item_list(Session *s, dumb_ptr<map_session_data> sd,
count = 0;
for (IOff0 i : IOff0::iter())
{
- if (pl_sd->status.inventory[i].nameid
- && (item_data =
- itemdb_search(pl_sd->status.inventory[i].nameid)) !=
- nullptr)
+ if (!pl_sd->status.inventory[i].nameid)
+ continue;
+ P<struct item_data> item_data = TRY_UNWRAP(itemdb_exists(pl_sd->status.inventory[i].nameid), continue);
+
{
counter = counter + pl_sd->status.inventory[i].amount;
count++;
@@ -3966,8 +4014,6 @@ static
ATCE atcommand_character_storage_list(Session *s, dumb_ptr<map_session_data> sd,
ZString message)
{
- Storage *stor;
- struct item_data *item_data = nullptr;
int count, counter;
CharName character;
@@ -3980,50 +4026,56 @@ ATCE atcommand_character_storage_list(Session *s, dumb_ptr<map_session_data> sd,
if (pc_isGM(sd).overwhelms(pc_isGM(pl_sd)))
{
// you can look items only lower or same level
- if ((stor = account2storage2(pl_sd->status_key.account_id)) != nullptr)
+ Option<P<Storage>> stor_ = account2storage2(pl_sd->status_key.account_id);
+ OMATCH_BEGIN (stor_)
{
- counter = 0;
- count = 0;
- for (SOff0 i : SOff0::iter())
+ OMATCH_CASE_SOME (stor)
{
- if (stor->storage_[i].nameid
- && (item_data =
- itemdb_search(stor->storage_[i].nameid)) != nullptr)
+ counter = 0;
+ count = 0;
+ for (SOff0 i : SOff0::iter())
{
- counter = counter + stor->storage_[i].amount;
- count++;
- if (count == 1)
+ if (!stor->storage_[i].nameid)
+ continue;
+ P<struct item_data> item_data = TRY_UNWRAP(itemdb_exists(stor->storage_[i].nameid), continue);
+
{
- AString output = STRPRINTF(
- "------ Storage items list of '%s' ------"_fmt,
- pl_sd->status_key.name);
+ counter = counter + stor->storage_[i].amount;
+ count++;
+ if (count == 1)
+ {
+ AString output = STRPRINTF(
+ "------ Storage items list of '%s' ------"_fmt,
+ pl_sd->status_key.name);
+ clif_displaymessage(s, output);
+ }
+ AString output;
+ if (true)
+ output = STRPRINTF("%d %s (%s, id: %d)"_fmt,
+ stor->storage_[i].amount,
+ item_data->name, item_data->jname,
+ stor->storage_[i].nameid);
clif_displaymessage(s, output);
}
- AString output;
- if (true)
- output = STRPRINTF("%d %s (%s, id: %d)"_fmt,
- stor->storage_[i].amount,
- item_data->name, item_data->jname,
- stor->storage_[i].nameid);
+ }
+ if (count == 0)
+ clif_displaymessage(s,
+ "No item found in the storage of this player."_s);
+ else
+ {
+ AString output = STRPRINTF(
+ "%d item(s) found in %d kind(s) of items."_fmt,
+ counter, count);
clif_displaymessage(s, output);
}
}
- if (count == 0)
- clif_displaymessage(s,
- "No item found in the storage of this player."_s);
- else
+ OMATCH_CASE_NONE ()
{
- AString output = STRPRINTF(
- "%d item(s) found in %d kind(s) of items."_fmt,
- counter, count);
- clif_displaymessage(s, output);
+ clif_displaymessage(s, "This player has no storage."_s);
+ return ATCE::OKAY;
}
}
- else
- {
- clif_displaymessage(s, "This player has no storage."_s);
- return ATCE::OKAY;
- }
+ OMATCH_END ();
}
else
{
@@ -4041,81 +4093,40 @@ ATCE atcommand_character_storage_list(Session *s, dumb_ptr<map_session_data> sd,
}
static
-ATCE atcommand_killer(Session *s, dumb_ptr<map_session_data> sd,
+ATCE atcommand_pvp(Session *s, dumb_ptr<map_session_data> sd,
ZString)
{
- sd->special_state.killer = !sd->special_state.killer;
-
- if (sd->special_state.killer)
- clif_displaymessage(s, "You be a killa..."_s);
- else
- clif_displaymessage(s, "You gonna be own3d..."_s);
-
- return ATCE::OKAY;
-}
-
-static
-ATCE atcommand_charkiller(Session *s, dumb_ptr<map_session_data>,
- ZString message)
-{
- CharName character;
-
- if (!asplit(message, &character))
- return ATCE::USAGE;
-
- dumb_ptr<map_session_data> pl_sd = map_nick2sd(character);
- if (pl_sd == nullptr)
- return ATCE::EXIST;
-
- pl_sd->special_state.killer = !pl_sd->special_state.killer;
+ int chan = sd->state.pvpchannel;
+ if (sd->pvp_timer || (chan > 1))
+ return ATCE::OKAY;
- if (pl_sd->special_state.killer)
- {
- clif_displaymessage(s, "The player is now a killer"_s);
- clif_displaymessage(pl_sd->sess, "You are now a killer"_s);
- }
- else
- {
- clif_displaymessage(s, "The player is no longer a killer"_s);
- clif_displaymessage(pl_sd->sess, "You are no longer a killer"_s);
+ if (chan < 1) {
+ sd->state.pvpchannel = 1;
+ clif_displaymessage(s, "##3PvP : ##BOn"_s);
+ } else {
+ sd->state.pvpchannel = 0;
+ clif_displaymessage(s, "##3PvP : ##BOff"_s);
}
+ pc_setpvptimer(sd, battle_config.player_pvp_time);
return ATCE::OKAY;
}
static
-ATCE atcommand_killable(Session *s, dumb_ptr<map_session_data> sd,
- ZString)
-{
- sd->special_state.killable = !sd->special_state.killable;
-
- if (sd->special_state.killable)
- clif_displaymessage(s, "You gonna be own3d..."_s);
- else
- clif_displaymessage(s, "You be a killa..."_s);
-
- return ATCE::OKAY;
-}
-
-static
-ATCE atcommand_charkillable(Session *s, dumb_ptr<map_session_data>,
+ATCE atcommand_charpvp(Session *, dumb_ptr<map_session_data>,
ZString message)
{
CharName character;
+ int channel;
- if (!asplit(message, &character))
+ if (!extract(message, record<' '>(&character, &channel)))
return ATCE::USAGE;
dumb_ptr<map_session_data> pl_sd = map_nick2sd(character);
if (pl_sd == nullptr)
return ATCE::EXIST;
- pl_sd->special_state.killable = !pl_sd->special_state.killable;
-
- if (pl_sd->special_state.killable)
- clif_displaymessage(s, "The player is now killable"_s);
- else
- clif_displaymessage(s, "The player is no longer killable"_s);
+ pl_sd->state.pvpchannel = channel;
return ATCE::OKAY;
}
@@ -4155,13 +4166,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;
@@ -4433,14 +4452,15 @@ ATCE atcommand_adjcmdlvl(Session *s, dumb_ptr<map_session_data>,
return ATCE::USAGE;
}
- AtCommandInfo *it = atcommand_info.search(cmd);
+ Option<P<AtCommandInfo>> it_ = atcommand_info.search(cmd);
{
- if (it)
+ OMATCH_BEGIN_SOME (it, it_)
{
it->level = newlev;
clif_displaymessage(s, "@command level changed."_s);
return ATCE::OKAY;
}
+ OMATCH_END ();
}
clif_displaymessage(s, "@command not found."_s);
@@ -4665,15 +4685,15 @@ ATCE atcommand_jump_iterate(Session *s, dumb_ptr<map_session_data> sd,
pl_sd = get_start();
}
- if (pl_sd->bl_m && pl_sd->bl_m->flag.get(MapFlag::NOWARPTO)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (pl_sd->bl_m->flag.get(MapFlag::NOWARPTO)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp you to the map of this player."_s);
return ATCE::PERM;
}
- if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARP)
- && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))))
+ if (sd->bl_m->flag.get(MapFlag::NOWARP)
+ && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)))
{
clif_displaymessage(s,
"You are not authorised to warp you from your actual map."_s);
@@ -4744,11 +4764,11 @@ ATCE atcommand_skillpool_info(Session *s, dumb_ptr<map_session_data>,
clif_displaymessage(s, buf);
}
- buf = STRPRINTF("Learned skills out of %d for %s:"_fmt,
- skill_pool_skills_size, character);
+ buf = STRPRINTF("Learned skills out of %zu for %s:"_fmt,
+ skill_pool_skills.size(), character);
clif_displaymessage(s, buf);
- for (i = 0; i < skill_pool_skills_size; ++i)
+ for (i = 0; i < skill_pool_skills.size(); ++i)
{
const RString& name = skill_name(skill_pool_skills[i]);
int lvl = pl_sd->status.skill[skill_pool_skills[i]].lv;
@@ -4919,14 +4939,10 @@ static
ATCE atcommand_source(Session *s, dumb_ptr<map_session_data>,
ZString)
{
- clif_displaymessage(s,
- "This server code consists of Free Software under GPL3&AGPL3"_s);
- clif_displaymessage(s,
- "This is commit " VERSION_HASH ", also known as " VERSION_FULL ""_s);
- clif_displaymessage(s,
- "The version is " VERSION_STRING ""_s);
- clif_displaymessage(s,
- "For source, see " VENDOR_SOURCE ""_s);
+ clif_displaymessage(s, VERSION_INFO_HEADER);
+ clif_displaymessage(s, VERSION_INFO_COMMIT);
+ clif_displaymessage(s, VERSION_INFO_NUMBER);
+ clif_displaymessage(s, VERSION_INFO_URL);
return ATCE::OKAY;
}
@@ -4954,6 +4970,9 @@ Map<XString, AtCommandInfo> atcommand_info =
{"goto"_s, {"<charname>"_s,
40, atcommand_goto,
"Warp yourself to another character"_s}},
+ {"npc"_s, {"<npc>"_s,
+ 40, atcommand_npc,
+ "Warp yourself to a npc"_s}},
{"jump"_s, {"[x] [y]"_s,
40, atcommand_jump,
"Warp yourself within a map"_s}},
@@ -5023,12 +5042,12 @@ Map<XString, AtCommandInfo> atcommand_info =
{"jlvl"_s, {"<delta>"_s,
60, atcommand_joblevelup,
"Adjust your job level"_s}},
- {"gm"_s, {"<password>"_s,
- 100, atcommand_gm,
- "Receive GM powers"_s}},
{"pvpoff"_s, {""_s,
60, atcommand_pvpoff,
"Enable PvP on your map"_s}},
+ {"exprate"_s, {"<percent>"_s,
+ 60, atcommand_exprate,
+ "Set base job/exp rate"_s}},
{"pvpon"_s, {""_s,
60, atcommand_pvpon,
"Disable PvP on your map"_s}},
@@ -5242,21 +5261,15 @@ Map<XString, AtCommandInfo> atcommand_info =
{"addwarp"_s, {"<mapname> <x> <y>"_s,
80, atcommand_addwarp,
"Create a new permanent warp"_s}},
- {"killer"_s, {""_s,
- 60, atcommand_killer,
- "Toggle whether you are a killer"_s}},
- {"charkiller"_s, {"<charname>"_s,
- 60, atcommand_charkiller,
- "Toggle whether a player is a killer"_s}},
+ {"pvp"_s, {""_s,
+ 0, atcommand_pvp,
+ "Toggle your pvp flag"_s}},
{"npcmove"_s, {"<x> <y> <npc-name>"_s,
80, atcommand_npcmove,
"Force an NPC to move on the map"_s}},
- {"killable"_s, {""_s,
- 60, atcommand_killable,
- "Toggle whether you are killable"_s}},
- {"charkillable"_s, {"<charname>"_s,
- 60, atcommand_charkillable,
- "Toggle whether a player is killable"_s}},
+ {"charpvp"_s, {"<charname> <channel>"_s,
+ 40, atcommand_charpvp,
+ "Set the pvp channel of another player"_s}},
{"chareffect"_s, {"<type> <target>"_s,
40, atcommand_chareffect,
"Apply effect type with arg 0 to a player"_s}},
@@ -5357,4 +5370,5 @@ Map<XString, AtCommandInfo> atcommand_info =
0, atcommand_source,
"Legal information about source code (must be a level 0 command!)"_s}},
};
+} // namespace map
} // namespace tmwa
diff --git a/src/map/atcommand.hpp b/src/map/atcommand.hpp
index 4bf5277..4c0e421 100644
--- a/src/map/atcommand.hpp
+++ b/src/map/atcommand.hpp
@@ -22,17 +22,11 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
-#include "../generic/fwd.hpp"
-
-#include "../net/fwd.hpp"
-
-#include "../mmo/fwd.hpp"
-
namespace tmwa
{
+namespace map
+{
bool is_atcommand(Session *s, dumb_ptr<map_session_data> sd,
ZString message, GmLevel gmlvl);
@@ -40,8 +34,6 @@ bool atcommand_config_read(ZString cfgName);
void log_atcommand(dumb_ptr<map_session_data> sd, ZString cmd);
-// only used by map.cpp
-extern AString gm_log;
-
void atcommand_config_write(ZString cfgName);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/battle.cpp b/src/map/battle.cpp
index 856408c..5b63acc 100644
--- a/src/map/battle.cpp
+++ b/src/map/battle.cpp
@@ -32,12 +32,17 @@
#include "../generic/random.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
#include "../io/read.hpp"
+#include "../io/span.hpp"
#include "../mmo/config_parse.hpp"
+#include "../mmo/cxxstdio_enums.hpp"
+#include "../high/utils.hpp"
+
+#include "battle_conf.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "itemdb.hpp"
#include "map.hpp"
#include "mob.hpp"
@@ -50,13 +55,8 @@
namespace tmwa
{
-static Battle_Config init_battle_config();
-
-DIAG_PUSH();
-DIAG_I(shadow);
-struct Battle_Config battle_config = init_battle_config();
-DIAG_POP();
-
+namespace map
+{
/*==========================================
* 自分をロックしている対象の数を返す(汎用)
* 戻りは整数で0以上
@@ -482,21 +482,6 @@ int battle_get_atk(dumb_ptr<block_list> bl)
}
/*==========================================
- * 対象の左手Atkを返す(汎用)
- * 戻りは整数で0以上
- *------------------------------------------
- */
-static
-int battle_get_atk_(dumb_ptr<block_list> bl)
-{
- nullpo_retz(bl);
- if (bl->bl_type == BL::PC)
- return bl->is_player()->watk_;
- else
- return 0;
-}
-
-/*==========================================
* 対象のAtk2を返す(汎用)
* 戻りは整数で0以上
*------------------------------------------
@@ -520,21 +505,6 @@ int battle_get_atk2(dumb_ptr<block_list> bl)
}
/*==========================================
- * 対象の左手Atk2を返す(汎用)
- * 戻りは整数で0以上
- *------------------------------------------
- */
-static
-int battle_get_atk_2(dumb_ptr<block_list> bl)
-{
- nullpo_retz(bl);
- if (bl->bl_type == BL::PC)
- return bl->is_player()->watk_2;
- else
- return 0;
-}
-
-/*==========================================
* 対象のMAtk1を返す(汎用)
* 戻りは整数で0以上
*------------------------------------------
@@ -756,7 +726,7 @@ interval_t battle_get_adelay(dumb_ptr<block_list> bl)
if (aspd_rate != 100)
adelay = adelay * aspd_rate / 100;
- return std::max(adelay, static_cast<interval_t>(battle_config.monster_max_aspd) * 2);
+ return std::max(adelay, battle_config.monster_max_aspd * 2);
}
}
@@ -771,7 +741,7 @@ interval_t battle_get_amotion(dumb_ptr<block_list> bl)
interval_t amotion = 2_s;
int aspd_rate = 100;
if (bl->bl_type == BL::MOB)
- amotion = static_cast<interval_t>(get_mob_db(bl->is_mob()->mob_class).amotion);
+ amotion = get_mob_db(bl->is_mob()->mob_class).amotion;
if (sc_data)
{
@@ -783,7 +753,7 @@ interval_t battle_get_amotion(dumb_ptr<block_list> bl)
if (aspd_rate != 100)
amotion = amotion * aspd_rate / 100;
- return std::max(amotion, static_cast<interval_t>(battle_config.monster_max_aspd));
+ return std::max(amotion, battle_config.monster_max_aspd);
}
}
@@ -792,7 +762,7 @@ interval_t battle_get_dmotion(dumb_ptr<block_list> bl)
nullpo_retr(interval_t::zero(), bl);
if (bl->bl_type == BL::MOB)
{
- return static_cast<interval_t>(get_mob_db(bl->is_mob()->mob_class).dmotion);
+ return get_mob_db(bl->is_mob()->mob_class).dmotion;
}
else if (bl->bl_type == BL::PC)
{
@@ -884,16 +854,6 @@ eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> battle_
return nullptr;
}
-short *battle_get_sc_count(dumb_ptr<block_list> bl)
-{
- nullpo_retr(nullptr, bl);
- if (bl->bl_type == BL::MOB)
- return &bl->is_mob()->sc_count;
- else if (bl->bl_type == BL::PC)
- return &bl->is_player()->sc_count;
- return nullptr;
-}
-
Opt1 *battle_get_opt1(dumb_ptr<block_list> bl)
{
nullpo_retn(bl);
@@ -930,7 +890,7 @@ Opt3 *battle_get_opt3(dumb_ptr<block_list> bl)
return nullptr;
}
-Option *battle_get_option(dumb_ptr<block_list> bl)
+Opt0 *battle_get_option(dumb_ptr<block_list> bl)
{
nullpo_retn(bl);
if (bl->bl_type == BL::MOB)
@@ -1023,17 +983,6 @@ int battle_stopattack(dumb_ptr<block_list> bl)
return 0;
}
-// 移動停止
-int battle_stopwalking(dumb_ptr<block_list> bl, int type)
-{
- nullpo_retz(bl);
- if (bl->bl_type == BL::MOB)
- return mob_stop_walking(bl->is_mob(), type);
- else if (bl->bl_type == BL::PC)
- return pc_stop_walking(bl->is_player(), type);
- return 0;
-}
-
/*==========================================
* ダメージ最終計算
*------------------------------------------
@@ -1082,7 +1031,7 @@ struct Damage battle_calc_mob_weapon_attack(dumb_ptr<block_list> src,
int def2 = battle_get_def2(target);
int t_vit = battle_get_vit(target);
struct Damage wd {};
- int damage, damage2 = 0;
+ int damage;
DamageType type;
int div_;
BF flag;
@@ -1112,7 +1061,7 @@ struct Damage battle_calc_mob_weapon_attack(dumb_ptr<block_list> src,
|| battle_config.vit_penaly_type > 0)
target_count +=
battle_counttargeted(target, src,
- ATK(battle_config.agi_penaly_count_lv)); // FIXME
+ battle_config.agi_penaly_count_lv);
if (battle_config.agi_penaly_type > 0)
{
if (target_count >= battle_config.agi_penaly_count)
@@ -1198,7 +1147,7 @@ struct Damage battle_calc_mob_weapon_attack(dumb_ptr<block_list> src,
int t_def;
target_count =
1 + battle_counttargeted(target, src,
- ATK(battle_config.vit_penaly_count_lv)); // FIXME
+ battle_config.vit_penaly_count_lv);
if (battle_config.vit_penaly_type > 0)
{
if (target_count >= battle_config.vit_penaly_count)
@@ -1273,7 +1222,7 @@ struct Damage battle_calc_mob_weapon_attack(dumb_ptr<block_list> src,
if (type == DamageType::NORMAL && !random_::chance({hitrate, 100}))
{
- damage = damage2 = 0;
+ damage = 0;
dmg_lv = ATK::FLEE;
}
else
@@ -1312,7 +1261,6 @@ struct Damage battle_calc_mob_weapon_attack(dumb_ptr<block_list> src,
skill_num, skill_lv, flag);
wd.damage = damage;
- wd.damage2 = 0;
wd.type = type;
wd.div_ = div_;
wd.amotion = battle_get_amotion(src);
@@ -1357,14 +1305,13 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
int def2 = battle_get_def2(target);
int t_vit = battle_get_vit(target);
struct Damage wd {};
- int damage, damage2;
+ int damage;
DamageType type;
int div_;
BF flag;
ATK dmg_lv = ATK::ZERO;
eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data, t_sc_data;
- int atkmax_ = 0, atkmin_ = 0; //二刀流用
- int watk, watk_;
+ int watk;
bool da = false;
int ac_flag = 0;
int target_distance;
@@ -1392,7 +1339,7 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
flee = battle_get_flee(target);
if (battle_config.agi_penaly_type > 0 || battle_config.vit_penaly_type > 0) //AGI、VITペナルティ設定が有効
target_count += battle_counttargeted(target, src,
- ATK(battle_config.agi_penaly_count_lv)); //対象の数を算出
+ battle_config.agi_penaly_count_lv); //対象の数を算出
if (battle_config.agi_penaly_type > 0)
{
if (target_count >= battle_config.agi_penaly_count)
@@ -1428,13 +1375,12 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
dex = battle_get_dex(src); //DEX
watk = battle_get_atk(src); //ATK
- watk_ = battle_get_atk_(src); //ATK左手
type = DamageType::NORMAL;
div_ = 1; // single attack
{
- damage = damage2 = battle_get_baseatk(sd); //damega,damega2初登場、base_atkの取得
+ damage = battle_get_baseatk(sd); //damega,damega2初登場、base_atkの取得
}
if (sd->attackrange > 2)
{ // [fate] ranged weapon?
@@ -1443,22 +1389,21 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
damage * (256 +
((range_damage_bonus * target_distance) /
sd->attackrange)) >> 8;
- damage2 =
- damage2 * (256 +
- ((range_damage_bonus * target_distance) /
- sd->attackrange)) >> 8;
}
- atkmin = atkmin_ = dex; //最低ATKはDEXで初期化?
+ atkmin = dex; //最低ATKはDEXで初期化?
sd->state.arrow_atk = 0; //arrow_atk初期化
IOff0 widx = sd->equip_index_maybe[EQUIP::WEAPON];
- IOff0 sidx = sd->equip_index_maybe[EQUIP::SHIELD];
- if (widx.ok() && sd->inventory_data[widx])
- atkmin = atkmin * (80 + sd->inventory_data[widx]->wlv * 20) / 100;
- if (sidx.ok() && sd->inventory_data[sidx])
- atkmin_ = atkmin_ * (80 + sd->inventory_data[sidx]->wlv * 20) / 100;
+ if (widx.ok())
+ {
+ OMATCH_BEGIN_SOME (sdidw, sd->inventory_data[widx])
+ {
+ atkmin = atkmin * (80 + sdidw->wlv * 20) / 100;
+ }
+ OMATCH_END ();
+ }
if (sd->status.weapon == ItemLook::BOW)
{ //武器が弓矢の場合
atkmin = watk * ((atkmin < watk) ? atkmin : watk) / 100; //弓用最低ATK計算
@@ -1468,13 +1413,10 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
{
atkmax = watk;
- atkmax_ = watk_;
}
if (atkmin > atkmax && !(sd->state.arrow_atk))
atkmin = atkmax; //弓は最低が上回る場合あり
- if (atkmin_ > atkmax_)
- atkmin_ = atkmax_;
if (sd->double_rate > 0 && skill_num == SkillID::ZERO && skill_lv >= 0)
da = random_::chance({sd->double_rate, 100});
@@ -1486,9 +1428,6 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
if (sd->state.arrow_atk)
cri += sd->arrow_cri;
- if (sd->status.weapon == ItemLook::_16)
- // カタールの場合、クリティカルを倍に
- cri <<= 1;
cri -= battle_get_luk(target) * 3;
if (ac_flag)
cri = 1000;
@@ -1503,11 +1442,9 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
&& random_::chance({cri, 1000}))
{
damage += atkmax;
- damage2 += atkmax_;
if (sd->atk_rate != 100)
{
damage = (damage * sd->atk_rate) / 100;
- damage2 = (damage2 * sd->atk_rate) / 100;
}
if (sd->state.arrow_atk)
damage += sd->arrow_atk;
@@ -1521,14 +1458,9 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
damage += random_::in(atkmin, atkmax);
else
damage += atkmin;
- if (atkmax_ > atkmin_)
- damage2 += random_::in(atkmin_, atkmax_);
- else
- damage2 += atkmin_;
if (sd->atk_rate != 100)
{
damage = (damage * sd->atk_rate) / 100;
- damage2 = (damage2 * sd->atk_rate) / 100;
}
if (sd->state.arrow_atk)
@@ -1551,7 +1483,7 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
int t_def;
target_count =
1 + battle_counttargeted(target, src,
- ATK(battle_config.vit_penaly_count_lv)); // FIXME
+ battle_config.vit_penaly_count_lv);
if (battle_config.vit_penaly_type > 0)
{
if (target_count >= battle_config.vit_penaly_count)
@@ -1614,28 +1546,17 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
damage -= random_::in(0, vitbonusmax);
}
}
- {
- {
- damage2 = damage2 * (100 - def1) / 100;
- damage2 -= t_def;
- if (vitbonusmax > 0)
- damage2 -= random_::in(0, vitbonusmax);
- }
- }
}
}
}
// 精錬ダメージの追加
{ //DEF, VIT無視
damage += battle_get_atk2(src);
- damage2 += battle_get_atk_2(src);
}
// 0未満だった場合1に補正
if (damage < 1)
damage = 1;
- if (damage2 < 1)
- damage2 = 1;
// スキル修正2(修練系)
// 修練ダメージ(右手のみ) ソニックブロー時は別処理(1撃に付き1/8適応)
@@ -1652,7 +1573,7 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
hitrate = (hitrate < 5) ? 5 : hitrate;
if (type == DamageType::NORMAL && !random_::chance({hitrate, 100}))
{
- damage = damage2 = 0;
+ damage = 0;
dmg_lv = ATK::FLEE;
}
else
@@ -1662,37 +1583,6 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
if (damage < 0)
damage = 0;
- if (damage2 < 0)
- damage2 = 0;
-
- // >二刀流の左右ダメージ計算誰かやってくれぇぇぇぇえええ!
- // >map_session_data に左手ダメージ(atk,atk2)追加して
- // >pc_calcstatus()でやるべきかな?
- // map_session_data に左手武器(atk,atk2,ele,star,atkmods)追加して
- // pc_calcstatus()でデータを入力しています
-
- //左手のみ武器装備
- if (sd->weapontype1 == ItemLook::NONE
- && sd->weapontype2 != ItemLook::NONE)
- {
- damage = damage2;
- damage2 = 0;
- }
- // 右手、左手修練の適用
- if (sd->status.weapon >= ItemLook::SINGLE_HANDED_COUNT)
- { // 二刀流か?
- int dmg = damage, dmg2 = damage2;
- // 右手修練(60% 〜 100%) 右手全般
- damage = damage * 50 / 100;
- if (dmg > 0 && damage < 1)
- damage = 1;
- // 左手修練(40% 〜 80%) 左手全般
- damage2 = damage2 * 30 / 100;
- if (dmg2 > 0 && damage2 < 1)
- damage2 = 1;
- }
- else //二刀流でなければ左手ダメージは0
- damage2 = 0;
// 右手,短剣のみ
if (da)
@@ -1702,19 +1592,11 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
type = DamageType::DOUBLED;
}
- if (sd->status.weapon == ItemLook::_16)
- {
- // カタール追撃ダメージ
- damage2 = damage * 1 / 100;
- if (damage > 0 && damage2 < 1)
- damage2 = 1;
- }
-
// 完全回避の判定
if (skill_num == SkillID::ZERO && skill_lv >= 0 && tsd != nullptr && div_ < 255
&& random_::chance({battle_get_flee2(target), 1000}))
{
- damage = damage2 = 0;
+ damage = 0;
type = DamageType::FLEE2;
dmg_lv = ATK::LUCKY;
}
@@ -1725,7 +1607,7 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
if (skill_num == SkillID::ZERO && skill_lv >= 0 && tmd != nullptr && div_ < 255
&& random_::chance({battle_get_flee2(target), 1000}))
{
- damage = damage2 = 0;
+ damage = 0;
type = DamageType::FLEE2;
dmg_lv = ATK::LUCKY;
}
@@ -1736,35 +1618,18 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
{
if (damage > 0)
damage = 1;
- if (damage2 > 0)
- damage2 = 1;
}
- if (damage > 0 || damage2 > 0)
+ if (damage > 0)
{
- if (damage2 < 1) // ダメージ最終修正
+ {
damage =
battle_calc_damage(src, target, damage, div_, skill_num,
skill_lv, flag);
- else if (damage < 1) // 右手がミス?
- damage2 =
- battle_calc_damage(src, target, damage2, div_, skill_num,
- skill_lv, flag);
- else
- { // 両 手/カタールの場合はちょっと計算ややこしい
- int d1 = damage + damage2, d2 = damage2;
- damage =
- battle_calc_damage(src, target, damage + damage2, div_,
- skill_num, skill_lv, flag);
- damage2 = (d2 * 100 / d1) * damage / 100;
- if (damage > 1 && damage2 < 1)
- damage2 = 1;
- damage -= damage2;
}
}
wd.damage = damage;
- wd.damage2 = damage2;
wd.type = type;
wd.div_ = div_;
wd.amotion = battle_get_amotion(src);
@@ -1872,7 +1737,6 @@ struct Damage battle_calc_magic_attack(dumb_ptr<block_list> bl,
md.div_ = div_;
md.amotion = battle_get_amotion(bl);
md.dmotion = battle_get_dmotion(target);
- md.damage2 = 0;
md.type = DamageType::NORMAL;
md.flag = aflag;
@@ -1937,7 +1801,6 @@ struct Damage battle_calc_misc_attack(dumb_ptr<block_list> bl,
md.div_ = div_;
md.amotion = battle_get_amotion(bl);
md.dmotion = battle_get_dmotion(target);
- md.damage2 = 0;
md.type = DamageType::NORMAL;
md.flag = aflag;
return md;
@@ -2048,13 +1911,7 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target,
{
clif_damage(src, target, tick, wd.amotion, wd.dmotion,
- wd.damage, wd.div_, wd.type, wd.damage2);
- if (sd
- && (sd->status.weapon == ItemLook::_16
- || sd->status.weapon >= ItemLook::SINGLE_HANDED_COUNT)
- && wd.damage2 == 0)
- clif_damage(src, target, tick + 10_ms,
- wd.amotion, wd.dmotion, 0, 1, DamageType::NORMAL, 0);
+ wd.damage, wd.div_, wd.type);
}
MapBlockLock lock;
@@ -2063,9 +1920,17 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target,
{
IOff0 weapon_index = sd->equip_index_maybe[EQUIP::WEAPON];
ItemNameId weapon;
- if (weapon_index.ok() && sd->inventory_data[weapon_index]
- && bool(sd->status.inventory[weapon_index].equip & EPOS::WEAPON))
- weapon = sd->inventory_data[weapon_index]->nameid;
+ if (weapon_index.ok())
+ {
+ OMATCH_BEGIN_SOME (sdidw, sd->inventory_data[weapon_index])
+ {
+ if (bool(sd->status.inventory[weapon_index].equip & EPOS::WEAPON))
+ {
+ weapon = sdidw->nameid;
+ }
+ }
+ OMATCH_END ();
+ }
MAP_LOG("PC%d %s:%d,%d WPNDMG %s%d %d FOR %d WPN %d"_fmt,
sd->status_key.char_id, src->bl_m->name_, src->bl_x, src->bl_y,
@@ -2074,7 +1939,7 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target,
? unwrap<CharId>(target->is_player()->status_key.char_id)
: unwrap<BlockId>(target->bl_id),
battle_get_class(target),
- wd.damage + wd.damage2, weapon);
+ wd.damage, weapon);
}
if (target->bl_type == BL::PC)
@@ -2087,16 +1952,16 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target,
? unwrap<CharId>(src->is_player()->status_key.char_id)
: unwrap<BlockId>(src->bl_id),
battle_get_class(src),
- wd.damage + wd.damage2);
+ wd.damage);
}
- battle_damage(src, target, (wd.damage + wd.damage2), 0);
+ battle_damage(src, target, (wd.damage), 0);
if (target->bl_prev != nullptr &&
(target->bl_type != BL::PC
|| (target->bl_type == BL::PC
&& !pc_isdead(target->is_player()))))
{
- if (wd.damage > 0 || wd.damage2 > 0)
+ if (wd.damage > 0)
{
skill_additional_effect(src, target, SkillID::ZERO, 0);
}
@@ -2105,7 +1970,7 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target,
{
if (bool(wd.flag & BF::WEAPON)
&& src != target
- && (wd.damage > 0 || wd.damage2 > 0))
+ && (wd.damage > 0))
{
int hp = 0, sp = 0;
if (sd->hp_drain_rate && wd.damage > 0
@@ -2113,21 +1978,11 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target,
{
hp += (wd.damage * sd->hp_drain_per) / 100;
}
- if (sd->hp_drain_rate_ && wd.damage2 > 0
- && random_::chance({sd->hp_drain_rate_, 100}))
- {
- hp += (wd.damage2 * sd->hp_drain_per_) / 100;
- }
if (sd->sp_drain_rate && wd.damage > 0
&& random_::chance({sd->sp_drain_rate, 100}))
{
sp += (wd.damage * sd->sp_drain_per) / 100;
}
- if (sd->sp_drain_rate_ && wd.damage2 > 0
- && random_::chance({sd->sp_drain_rate_, 100}))
- {
- sp += (wd.damage2 * sd->sp_drain_per_) / 100;
- }
if (hp || sp)
pc_heal(sd, hp, sp);
}
@@ -2310,380 +2165,5 @@ int battle_check_range(dumb_ptr<block_list> src, dumb_ptr<block_list> bl,
return (path_search(&wpd, src->bl_m, src->bl_x + dx, src->bl_y + dy,
bl->bl_x - dx, bl->bl_y - dy, 0x10001) != -1) ? 1 : 0;
}
-
-Battle_Config init_battle_config()
-{
- DIAG_PUSH();
- DIAG_I(shadow);
- Battle_Config battle_config;
- DIAG_POP();
- {
- battle_config.warp_point_debug = 0;
- battle_config.enemy_critical = 0;
- battle_config.enemy_critical_rate = 100;
- battle_config.enemy_str = 1;
- battle_config.enemy_perfect_flee = 0;
- battle_config.casting_rate = 100;
- battle_config.delay_rate = 100;
- battle_config.delay_dependon_dex = 0;
- battle_config.skill_delay_attack_enable = 0;
- battle_config.monster_skill_add_range = 0;
- battle_config.player_damage_delay = 1;
- battle_config.flooritem_lifetime = std::chrono::duration_cast<std::chrono::milliseconds>(LIFETIME_FLOORITEM).count();
- battle_config.item_auto_get = 0;
- battle_config.drop_pickup_safety_zone = 20;
- battle_config.item_first_get_time = 3000;
- battle_config.item_second_get_time = 1000;
- battle_config.item_third_get_time = 1000;
-
- battle_config.base_exp_rate = 100;
- battle_config.job_exp_rate = 100;
- battle_config.death_penalty_type = 0;
- battle_config.death_penalty_base = 0;
- battle_config.death_penalty_job = 0;
- battle_config.restart_hp_rate = 0;
- battle_config.restart_sp_rate = 0;
- battle_config.monster_hp_rate = 100;
- battle_config.monster_max_aspd = 199;
- battle_config.atcommand_gm_only = 0;
- battle_config.gm_all_equipment = 0;
- battle_config.monster_active_enable = 1;
- battle_config.mob_skill_use = 1;
- battle_config.mob_count_rate = 100;
- battle_config.basic_skill_check = 1;
- battle_config.player_invincible_time = 5000;
- battle_config.skill_min_damage = 0;
- battle_config.natural_healhp_interval = 6000;
- battle_config.natural_healsp_interval = 8000;
- battle_config.natural_heal_skill_interval = 10000;
- battle_config.natural_heal_weight_rate = 50;
- battle_config.itemheal_regeneration_factor = 1;
- battle_config.arrow_decrement = 1;
- battle_config.max_aspd = 199;
- battle_config.max_hp = 32500;
- battle_config.max_sp = 32500;
- battle_config.max_lv = 99; // [MouseJstr]
- battle_config.max_parameter = 99;
- battle_config.monster_skill_log = 0;
- battle_config.battle_log = 0;
- battle_config.save_log = 0;
- battle_config.error_log = 1;
- battle_config.etc_log = 1;
- battle_config.save_clothcolor = 0;
- battle_config.undead_detect_type = 0;
- battle_config.agi_penaly_type = 0;
- battle_config.agi_penaly_count = 3;
- battle_config.agi_penaly_num = 0;
- battle_config.agi_penaly_count_lv = static_cast<int>(ATK::FLEE); // FIXME
- battle_config.vit_penaly_type = 0;
- battle_config.vit_penaly_count = 3;
- battle_config.vit_penaly_num = 0;
- battle_config.vit_penaly_count_lv = static_cast<int>(ATK::DEF); // FIXME
- battle_config.mob_changetarget_byskill = 0;
- battle_config.player_attack_direction_change = 1;
- battle_config.monster_attack_direction_change = 1;
- battle_config.display_delay_skill_fail = 1;
- battle_config.dead_branch_active = 0;
- battle_config.show_steal_in_same_party = 0;
- battle_config.hide_GM_session = 0;
- battle_config.invite_request_check = 1;
- battle_config.disp_experience = 0;
- battle_config.prevent_logout = 1; // Added by RoVeRT
- battle_config.maximum_level = 255; // Added by Valaris
- battle_config.drops_by_luk = 0; // [Valaris]
- battle_config.pk_mode = 0; // [Valaris]
- battle_config.multi_level_up = 0; // [Valaris]
- battle_config.hack_info_GM_level = 60; // added by [Yor] (default: 60, GM level)
- battle_config.any_warp_GM_min_level = 20; // added by [Yor]
- battle_config.min_hair_style = 0;
- battle_config.max_hair_style = 20;
- battle_config.min_hair_color = 0;
- battle_config.max_hair_color = 9;
- battle_config.min_cloth_color = 0;
- battle_config.max_cloth_color = 4;
-
- battle_config.castrate_dex_scale = 150;
-
- battle_config.area_size = 14;
-
- battle_config.chat_lame_penalty = 2;
- battle_config.chat_spam_threshold = 10;
- battle_config.chat_spam_flood = 10;
- battle_config.chat_spam_ban = 1;
- battle_config.chat_spam_warn = 8;
- battle_config.chat_maxline = 255;
-
- battle_config.packet_spam_threshold = 2;
- battle_config.packet_spam_flood = 30;
- battle_config.packet_spam_kick = 1;
-
- battle_config.mask_ip_gms = 1;
-
- battle_config.mob_splash_radius = -1;
- }
- return battle_config;
-}
-
-bool battle_config_read(ZString cfgName)
-{
- bool rv = true;
- io::ReadFile in(cfgName);
- if (!in.is_open())
- {
- PRINTF("file not found: %s\n"_fmt, cfgName);
- return false;
- }
-
- AString line;
- while (in.getline(line))
- {
-#define BATTLE_CONFIG_VAR(name) {#name##_s, &battle_config.name}
- const struct
- {
- LString str;
- int *val;
- } data[] =
- {
- BATTLE_CONFIG_VAR(warp_point_debug),
- BATTLE_CONFIG_VAR(enemy_critical),
- BATTLE_CONFIG_VAR(enemy_critical_rate),
- BATTLE_CONFIG_VAR(enemy_str),
- BATTLE_CONFIG_VAR(enemy_perfect_flee),
- BATTLE_CONFIG_VAR(casting_rate),
- BATTLE_CONFIG_VAR(delay_rate),
- BATTLE_CONFIG_VAR(delay_dependon_dex),
- BATTLE_CONFIG_VAR(skill_delay_attack_enable),
- BATTLE_CONFIG_VAR(monster_skill_add_range),
- BATTLE_CONFIG_VAR(player_damage_delay),
- BATTLE_CONFIG_VAR(flooritem_lifetime),
- BATTLE_CONFIG_VAR(item_auto_get),
- BATTLE_CONFIG_VAR(drop_pickup_safety_zone),
- BATTLE_CONFIG_VAR(item_first_get_time),
- BATTLE_CONFIG_VAR(item_second_get_time),
- BATTLE_CONFIG_VAR(item_third_get_time),
- BATTLE_CONFIG_VAR(base_exp_rate),
- BATTLE_CONFIG_VAR(job_exp_rate),
- BATTLE_CONFIG_VAR(death_penalty_type),
- BATTLE_CONFIG_VAR(death_penalty_base),
- BATTLE_CONFIG_VAR(death_penalty_job),
- BATTLE_CONFIG_VAR(restart_hp_rate),
- BATTLE_CONFIG_VAR(restart_sp_rate),
- BATTLE_CONFIG_VAR(monster_hp_rate),
- BATTLE_CONFIG_VAR(monster_max_aspd),
- BATTLE_CONFIG_VAR(atcommand_gm_only),
- BATTLE_CONFIG_VAR(atcommand_spawn_quantity_limit),
- BATTLE_CONFIG_VAR(gm_all_equipment),
- BATTLE_CONFIG_VAR(monster_active_enable),
- BATTLE_CONFIG_VAR(mob_skill_use),
- BATTLE_CONFIG_VAR(mob_count_rate),
- BATTLE_CONFIG_VAR(basic_skill_check),
- BATTLE_CONFIG_VAR(player_invincible_time),
- BATTLE_CONFIG_VAR(skill_min_damage),
- BATTLE_CONFIG_VAR(natural_healhp_interval),
- BATTLE_CONFIG_VAR(natural_healsp_interval),
- BATTLE_CONFIG_VAR(natural_heal_skill_interval),
- BATTLE_CONFIG_VAR(natural_heal_weight_rate),
- BATTLE_CONFIG_VAR(itemheal_regeneration_factor),
- BATTLE_CONFIG_VAR(arrow_decrement),
- BATTLE_CONFIG_VAR(max_aspd),
- BATTLE_CONFIG_VAR(max_hp),
- BATTLE_CONFIG_VAR(max_sp),
- BATTLE_CONFIG_VAR(max_lv),
- BATTLE_CONFIG_VAR(max_parameter),
- BATTLE_CONFIG_VAR(monster_skill_log),
- BATTLE_CONFIG_VAR(battle_log),
- BATTLE_CONFIG_VAR(save_log),
- BATTLE_CONFIG_VAR(error_log),
- BATTLE_CONFIG_VAR(etc_log),
- BATTLE_CONFIG_VAR(save_clothcolor),
- BATTLE_CONFIG_VAR(undead_detect_type),
- BATTLE_CONFIG_VAR(agi_penaly_type),
- BATTLE_CONFIG_VAR(agi_penaly_count),
- BATTLE_CONFIG_VAR(agi_penaly_num),
- BATTLE_CONFIG_VAR(agi_penaly_count_lv),
- BATTLE_CONFIG_VAR(vit_penaly_type),
- BATTLE_CONFIG_VAR(vit_penaly_count),
- BATTLE_CONFIG_VAR(vit_penaly_num),
- BATTLE_CONFIG_VAR(vit_penaly_count_lv),
- BATTLE_CONFIG_VAR(mob_changetarget_byskill),
- BATTLE_CONFIG_VAR(player_attack_direction_change),
- BATTLE_CONFIG_VAR(monster_attack_direction_change),
- BATTLE_CONFIG_VAR(display_delay_skill_fail),
- BATTLE_CONFIG_VAR(dead_branch_active),
- BATTLE_CONFIG_VAR(show_steal_in_same_party),
- BATTLE_CONFIG_VAR(hide_GM_session),
- BATTLE_CONFIG_VAR(invite_request_check),
- BATTLE_CONFIG_VAR(disp_experience),
- BATTLE_CONFIG_VAR(prevent_logout), // Added by RoVeRT
- BATTLE_CONFIG_VAR(alchemist_summon_reward), // [Valaris]
- BATTLE_CONFIG_VAR(maximum_level), // [Valaris]
- BATTLE_CONFIG_VAR(drops_by_luk), // [Valaris]
- BATTLE_CONFIG_VAR(monsters_ignore_gm), // [Valaris]
- BATTLE_CONFIG_VAR(pk_mode), // [Valaris]
- BATTLE_CONFIG_VAR(multi_level_up), // [Valaris]
- BATTLE_CONFIG_VAR(hack_info_GM_level), // added by [Yor]
- BATTLE_CONFIG_VAR(any_warp_GM_min_level), // added by [Yor]
- BATTLE_CONFIG_VAR(min_hair_style), // added by [MouseJstr]
- BATTLE_CONFIG_VAR(max_hair_style), // added by [MouseJstr]
- BATTLE_CONFIG_VAR(min_hair_color), // added by [MouseJstr]
- BATTLE_CONFIG_VAR(max_hair_color), // added by [MouseJstr]
- BATTLE_CONFIG_VAR(min_cloth_color), // added by [MouseJstr]
- BATTLE_CONFIG_VAR(max_cloth_color), // added by [MouseJstr]
- BATTLE_CONFIG_VAR(castrate_dex_scale), // added by [MouseJstr]
- BATTLE_CONFIG_VAR(area_size), // added by [MouseJstr]
- BATTLE_CONFIG_VAR(chat_lame_penalty),
- BATTLE_CONFIG_VAR(chat_spam_threshold),
- BATTLE_CONFIG_VAR(chat_spam_flood),
- BATTLE_CONFIG_VAR(chat_spam_ban),
- BATTLE_CONFIG_VAR(chat_spam_warn),
- BATTLE_CONFIG_VAR(chat_maxline),
- BATTLE_CONFIG_VAR(packet_spam_threshold),
- BATTLE_CONFIG_VAR(packet_spam_flood),
- BATTLE_CONFIG_VAR(packet_spam_kick),
- BATTLE_CONFIG_VAR(mask_ip_gms),
- BATTLE_CONFIG_VAR(mob_splash_radius),
- };
-
- if (is_comment(line))
- continue;
- XString w1;
- ZString w2;
- if (!config_split(line, &w1, &w2))
- {
- PRINTF("Bad config line: %s\n"_fmt, line);
- rv = false;
- continue;
- }
-
- if (w1 == "import"_s)
- {
- battle_config_read(w2);
- continue;
- }
-
- for (auto datum : data)
- if (w1 == datum.str)
- {
- *datum.val = config_switch(w2);
- goto continue_outer;
- }
-
- PRINTF("WARNING: unknown battle conf key: %s\n"_fmt, AString(w1));
- rv = false;
-
- continue_outer:
- ;
- }
-
- return rv;
-}
-
-void battle_config_check()
-{
- {
- if (static_cast<interval_t>(battle_config.flooritem_lifetime) < 1_s)
- battle_config.flooritem_lifetime = std::chrono::duration_cast<std::chrono::milliseconds>(LIFETIME_FLOORITEM).count();
- if (battle_config.restart_hp_rate < 0)
- battle_config.restart_hp_rate = 0;
- else if (battle_config.restart_hp_rate > 100)
- battle_config.restart_hp_rate = 100;
- if (battle_config.restart_sp_rate < 0)
- battle_config.restart_sp_rate = 0;
- else if (battle_config.restart_sp_rate > 100)
- battle_config.restart_sp_rate = 100;
- if (battle_config.natural_healhp_interval < NATURAL_HEAL_INTERVAL.count())
- battle_config.natural_healhp_interval = NATURAL_HEAL_INTERVAL.count();
- if (battle_config.natural_healsp_interval < NATURAL_HEAL_INTERVAL.count())
- battle_config.natural_healsp_interval = NATURAL_HEAL_INTERVAL.count();
- if (battle_config.natural_heal_skill_interval < NATURAL_HEAL_INTERVAL.count())
- battle_config.natural_heal_skill_interval = NATURAL_HEAL_INTERVAL.count();
- if (battle_config.natural_heal_weight_rate < 50)
- battle_config.natural_heal_weight_rate = 50;
- if (battle_config.natural_heal_weight_rate > 101)
- battle_config.natural_heal_weight_rate = 101;
- battle_config.monster_max_aspd =
- 2000 - battle_config.monster_max_aspd * 10;
- if (battle_config.monster_max_aspd < 10)
- battle_config.monster_max_aspd = 10;
- if (battle_config.monster_max_aspd > 1000)
- battle_config.monster_max_aspd = 1000;
- battle_config.max_aspd = 2000 - battle_config.max_aspd * 10;
- if (battle_config.max_aspd < 10)
- battle_config.max_aspd = 10;
- if (battle_config.max_aspd > 1000)
- battle_config.max_aspd = 1000;
- if (battle_config.max_hp > 1000000)
- battle_config.max_hp = 1000000;
- if (battle_config.max_hp < 100)
- battle_config.max_hp = 100;
- if (battle_config.max_sp > 1000000)
- battle_config.max_sp = 1000000;
- if (battle_config.max_sp < 100)
- battle_config.max_sp = 100;
- if (battle_config.max_parameter < 10)
- battle_config.max_parameter = 10;
- if (battle_config.max_parameter > 10000)
- battle_config.max_parameter = 10000;
-
- if (battle_config.agi_penaly_count < 2)
- battle_config.agi_penaly_count = 2;
- if (battle_config.vit_penaly_count < 2)
- battle_config.vit_penaly_count = 2;
-
- if (battle_config.hack_info_GM_level < 0) // added by [Yor]
- battle_config.hack_info_GM_level = 0;
- else if (battle_config.hack_info_GM_level > 100)
- battle_config.hack_info_GM_level = 100;
-
- if (battle_config.any_warp_GM_min_level < 0) // added by [Yor]
- battle_config.any_warp_GM_min_level = 0;
- else if (battle_config.any_warp_GM_min_level > 100)
- battle_config.any_warp_GM_min_level = 100;
-
- if (battle_config.chat_spam_ban < 0)
- battle_config.chat_spam_ban = 0;
- else if (battle_config.chat_spam_ban > 32767)
- battle_config.chat_spam_ban = 32767;
-
- if (battle_config.chat_spam_flood < 0)
- battle_config.chat_spam_flood = 0;
- else if (battle_config.chat_spam_flood > 32767)
- battle_config.chat_spam_flood = 32767;
-
- if (battle_config.chat_spam_warn < 0)
- battle_config.chat_spam_warn = 0;
- else if (battle_config.chat_spam_warn > 32767)
- battle_config.chat_spam_warn = 32767;
-
- if (battle_config.chat_spam_threshold < 0)
- battle_config.chat_spam_threshold = 0;
- else if (battle_config.chat_spam_threshold > 32767)
- battle_config.chat_spam_threshold = 32767;
-
- if (battle_config.chat_maxline < 1)
- battle_config.chat_maxline = 1;
- else if (battle_config.chat_maxline > 512)
- battle_config.chat_maxline = 512;
-
- if (battle_config.packet_spam_threshold < 0)
- battle_config.packet_spam_threshold = 0;
- else if (battle_config.packet_spam_threshold > 32767)
- battle_config.packet_spam_threshold = 32767;
-
- if (battle_config.packet_spam_flood < 0)
- battle_config.packet_spam_flood = 0;
- else if (battle_config.packet_spam_flood > 32767)
- battle_config.packet_spam_flood = 32767;
-
- if (battle_config.packet_spam_kick < 0)
- battle_config.packet_spam_kick = 0;
- else if (battle_config.packet_spam_kick > 1)
- battle_config.packet_spam_kick = 1;
-
- if (battle_config.mask_ip_gms < 0)
- battle_config.mask_ip_gms = 0;
- else if (battle_config.mask_ip_gms > 1)
- battle_config.mask_ip_gms = 1;
- }
-}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/battle.hpp b/src/map/battle.hpp
index 97a4a86..1a13420 100644
--- a/src/map/battle.hpp
+++ b/src/map/battle.hpp
@@ -20,27 +20,25 @@
// 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 "fwd.hpp"
-
#include "battle.t.hpp"
-#include "../strings/fwd.hpp"
-
-#include "../generic/fwd.hpp"
+#include "fwd.hpp"
#include "../net/timer.t.hpp"
-#include "clif.t.hpp"
+#include "../mmo/clif.t.hpp"
#include "map.t.hpp"
-#include "skill.t.hpp"
+#include "../mmo/skill.t.hpp"
namespace tmwa
{
+namespace map
+{
// ダメージ
struct Damage
{
- int damage, damage2;
+ int damage;
DamageType type;
int div_;
interval_t amotion, dmotion;
@@ -62,7 +60,6 @@ int battle_heal(dumb_ptr<block_list> bl, dumb_ptr<block_list> target, int hp,
// 攻撃や移動を止める
int battle_stopattack(dumb_ptr<block_list> bl);
-int battle_stopwalking(dumb_ptr<block_list> bl, int type);
// 通常攻撃処理まとめ
ATK battle_weapon_attack(dumb_ptr<block_list> bl, dumb_ptr<block_list> target,
@@ -101,128 +98,15 @@ MobMode battle_get_mode(dumb_ptr<block_list> bl);
int battle_get_stat(SP stat_id, dumb_ptr<block_list> bl);
eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> battle_get_sc_data(dumb_ptr<block_list> bl);
-short *battle_get_sc_count(dumb_ptr<block_list> bl);
Opt1 *battle_get_opt1(dumb_ptr<block_list> bl);
Opt2 *battle_get_opt2(dumb_ptr<block_list> bl);
Opt3 *battle_get_opt3(dumb_ptr<block_list> bl);
-Option *battle_get_option(dumb_ptr<block_list> bl);
+Opt0 *battle_get_option(dumb_ptr<block_list> bl);
bool battle_check_undead(Race race, Element element);
int battle_check_target(dumb_ptr<block_list> src, dumb_ptr<block_list> target,
BCT flag);
int battle_check_range(dumb_ptr<block_list> src, dumb_ptr<block_list> bl,
int range);
-
-extern struct Battle_Config
-{
- int warp_point_debug;
- int enemy_critical;
- int enemy_critical_rate;
- int enemy_str;
- int enemy_perfect_flee;
- int casting_rate, delay_rate, delay_dependon_dex;
- int skill_delay_attack_enable;
- int monster_skill_add_range;
- int player_damage_delay;
- int flooritem_lifetime;
- int item_auto_get;
- int item_first_get_time;
- int item_second_get_time;
- int item_third_get_time;
- int base_exp_rate, job_exp_rate;
- int death_penalty_type;
- int death_penalty_base, death_penalty_job;
- int restart_hp_rate;
- int restart_sp_rate;
- int monster_hp_rate;
- int monster_max_aspd;
- int atcommand_gm_only;
- int atcommand_spawn_quantity_limit;
- int gm_all_equipment;
- int monster_active_enable;
- int mob_skill_use;
- int mob_count_rate;
- int basic_skill_check;
- int player_invincible_time;
- int skill_min_damage;
- int natural_healhp_interval;
- int natural_healsp_interval;
- int natural_heal_skill_interval;
- int natural_heal_weight_rate;
- int arrow_decrement;
- int max_aspd;
- int max_hp;
- int max_sp;
- int max_lv;
- int max_parameter;
- int monster_skill_log;
- int battle_log;
- int save_log;
- int error_log;
- int etc_log;
- int save_clothcolor;
- int undead_detect_type;
- int agi_penaly_type;
- int agi_penaly_count;
- int agi_penaly_num;
- int vit_penaly_type;
- int vit_penaly_count;
- int vit_penaly_num;
- int mob_changetarget_byskill;
- int player_attack_direction_change;
- int monster_attack_direction_change;
- int display_delay_skill_fail;
- int dead_branch_active;
- int show_steal_in_same_party;
-
- int prevent_logout;
-
- int alchemist_summon_reward;
- int maximum_level;
- int drops_by_luk;
- int monsters_ignore_gm;
- int multi_level_up;
- int pk_mode;
-
- int agi_penaly_count_lv;
- int vit_penaly_count_lv;
-
- int hide_GM_session;
- int invite_request_check;
- int disp_experience;
-
- int hack_info_GM_level;
- int any_warp_GM_min_level;
-
- int min_hair_style;
- int max_hair_style;
- int min_hair_color;
- int max_hair_color;
- int min_cloth_color;
- int max_cloth_color;
-
- int castrate_dex_scale;
- int area_size;
-
- int chat_lame_penalty;
- int chat_spam_threshold;
- int chat_spam_flood;
- int chat_spam_ban;
- int chat_spam_warn;
- int chat_maxline;
-
- int packet_spam_threshold;
- int packet_spam_flood;
- int packet_spam_kick;
-
- int mask_ip_gms;
-
- int drop_pickup_safety_zone;
- int itemheal_regeneration_factor;
-
- int mob_splash_radius;
-} battle_config;
-
-bool battle_config_read(ZString cfgName);
-void battle_config_check();
+} // namespace map
} // namespace tmwa
diff --git a/src/map/battle.t.hpp b/src/map/battle.t.hpp
index 53c34ff..4759b68 100644
--- a/src/map/battle.t.hpp
+++ b/src/map/battle.t.hpp
@@ -29,6 +29,8 @@
namespace tmwa
{
+namespace map
+{
namespace e
{
enum class BF : uint16_t
@@ -241,4 +243,5 @@ earray<Races, Race, Race::COUNT> race_shift //=
Races::boss,
Races::other,
}};
+} // namespace map
} // namespace tmwa
diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp
index 0748f43..2606911 100644
--- a/src/map/chrif.cpp
+++ b/src/map/chrif.cpp
@@ -29,21 +29,25 @@
#include "../io/cxxstdio.hpp"
#include "../net/ip.hpp"
-#include "../net/packets.hpp"
#include "../net/socket.hpp"
#include "../net/timer.hpp"
+#include "../net/timestamp-utils.hpp"
#include "../proto2/char-map.hpp"
#include "../mmo/human_time_diff.hpp"
-#include "../mmo/mmo.hpp"
-#include "../mmo/utils.hpp"
+#include "../high/mmo.hpp"
+
+#include "../wire/packets.hpp"
#include "battle.hpp"
+#include "battle_conf.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "intif.hpp"
#include "itemdb.hpp"
#include "map.hpp"
+#include "map_conf.hpp"
#include "npc.hpp"
#include "pc.hpp"
#include "storage.hpp"
@@ -53,60 +57,8 @@
namespace tmwa
{
-Session *char_session;
-static
-IP4Address char_ip;
-static
-int char_port = 6121;
-static
-AccountName userid;
-static
-AccountPass passwd;
-static
-int chrif_state;
-
-// 設定ファイル読み込み関係
-/*==========================================
- *
- *------------------------------------------
- */
-void chrif_setuserid(AccountName id)
-{
- userid = id;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-void chrif_setpasswd(AccountPass pwd)
-{
- passwd = pwd;
-}
-
-AccountPass chrif_getpasswd(void)
-{
- return passwd;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-void chrif_setip(IP4Address ip)
+namespace map
{
- char_ip = ip;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-void chrif_setport(int port)
-{
- char_port = port;
-}
-
/*==========================================
*
*------------------------------------------
@@ -151,11 +103,11 @@ static
int chrif_connect(Session *s)
{
Packet_Fixed<0x2af8> fixed_f8;
- fixed_f8.account_name = userid;
- fixed_f8.account_pass = passwd;
+ fixed_f8.account_name = map_conf.userid;
+ fixed_f8.account_pass = map_conf.passwd;
fixed_f8.unused = 0;
- fixed_f8.ip = clif_getip();
- fixed_f8.port = clif_getport();
+ fixed_f8.ip = map_conf.map_ip;
+ fixed_f8.port = map_conf.map_port;
send_fpacket<0x2af8, 60>(s, fixed_f8);
return 0;
@@ -266,7 +218,7 @@ int chrif_changemapserverack(Session *, const Packet_Fixed<0x2b06>& fixed)
if (fixed.error == 1)
{
if (battle_config.error_log)
- PRINTF("map server change failed.\n"_fmt);
+ PRINTF("Changing the map server failed.\n"_fmt);
pc_authfail(sd->status_key.account_id);
return 0;
}
@@ -289,7 +241,7 @@ int chrif_connectack(Session *s, const Packet_Fixed<0x2af9>& fixed)
{
if (fixed.code)
{
- PRINTF("Connected to char-server failed %d.\n"_fmt, fixed.code);
+ PRINTF("Connecting to char-server failed %d.\n"_fmt, fixed.code);
exit(1);
}
PRINTF("Connected to char-server (connection #%d).\n"_fmt, s);
@@ -297,11 +249,6 @@ int chrif_connectack(Session *s, const Packet_Fixed<0x2af9>& fixed)
chrif_sendmap(s);
- PRINTF("chrif: OnCharIfInit event done. (%d events)\n"_fmt,
- npc_event_doall(stringish<ScriptLabel>("OnCharIfInit"_s)));
- PRINTF("chrif: OnInterIfInit event done. (%d events)\n"_fmt,
- npc_event_doall(stringish<ScriptLabel>("OnInterIfInit"_s)));
-
return 0;
}
@@ -314,13 +261,11 @@ int chrif_sendmapack(Session *, Packet_Fixed<0x2afb> fixed)
{
if (fixed.unknown) //impossible
{
- PRINTF("chrif : send map list to char server failed %d\n"_fmt,
+ PRINTF("chrif: sending the map list to char-server failed %d\n"_fmt,
fixed.unknown);
exit(1);
}
- wisp_server_name = fixed.whisper_name;
-
chrif_state = 2;
return 0;
@@ -395,23 +340,6 @@ int chrif_charselectreq(dumb_ptr<map_session_data> sd)
}
/*==========================================
- * GMに変化要求
- *------------------------------------------
- */
-void chrif_changegm(AccountId id, ZString pass)
-{
- if (!char_session)
- return;
-
- if (battle_config.etc_log)
- PRINTF("chrif_changegm: account: %d, password: '%s'.\n"_fmt, id, pass);
-
- Packet_Head<0x2b0a> head_0a;
- head_0a.account_id = id;
- send_vpacket<0x2b0a, 8, 1>(char_session, head_0a, pass);
-}
-
-/*==========================================
* Change Email
*------------------------------------------
*/
@@ -455,7 +383,7 @@ void chrif_char_ask_name(AccountId id, CharName character_name, short operation_
fixed_0e.operation = operation_type; // type of operation
if (operation_type == 2)
fixed_0e.ban_add = modif;
- PRINTF("chrif : sended 0x2b0e\n"_fmt);
+ PRINTF("chrif: sent 0x2b0e\n"_fmt);
send_fpacket<0x2b0e, 44>(char_session, fixed_0e);
}
@@ -469,7 +397,7 @@ void chrif_char_ask_name(AccountId id, CharName character_name, short operation_
* 4: unban
* 5: changesex
* type of answer:
- * 0: login-server resquest done
+ * 0: login-server request done
* 1: player not found
* 2: gm level too low
* 3: login-server offline
@@ -486,7 +414,7 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed)
{
AString output;
if (fixed.error == 1) // player not found
- output = STRPRINTF("The player '%s' doesn't exist."_fmt,
+ output = STRPRINTF("The player, '%s,' doesn't exist."_fmt,
player_name);
else
{
@@ -495,20 +423,20 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed)
case 1: // block
switch (fixed.error)
{
- case 0: // login-server resquest done
+ case 0: // login-server request done
output = STRPRINTF(
- "Login-server has been asked to block the player '%s'."_fmt,
+ "Login-server has been asked to block '%s'."_fmt,
player_name);
break;
//case 1: // player not found
case 2: // gm level too low
output = STRPRINTF(
- "Your GM level don't authorise you to block the player '%s'."_fmt,
+ "Your GM level doesn't authorize you to block the player '%s'."_fmt,
player_name);
break;
case 3: // login-server offline
output = STRPRINTF(
- "Login-server is offline. Impossible to block the the player '%s'."_fmt,
+ "Login-server is offline, so it's impossible to block '%s'."_fmt,
player_name);
break;
}
@@ -516,20 +444,20 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed)
case 2: // ban
switch (fixed.error)
{
- case 0: // login-server resquest done
+ case 0: // login-server request done
output = STRPRINTF(
- "Login-server has been asked to ban the player '%s'."_fmt,
+ "Login-server has been asked to ban '%s'."_fmt,
player_name);
break;
//case 1: // player not found
case 2: // gm level too low
output = STRPRINTF(
- "Your GM level don't authorise you to ban the player '%s'."_fmt,
+ "Your GM level doesn't authorize you to ban '%s'."_fmt,
player_name);
break;
case 3: // login-server offline
output = STRPRINTF(
- "Login-server is offline. Impossible to ban the the player '%s'."_fmt,
+ "Login-server is offline, so it's impossible to ban '%s'."_fmt,
player_name);
break;
}
@@ -537,20 +465,20 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed)
case 3: // unblock
switch (fixed.error)
{
- case 0: // login-server resquest done
+ case 0: // login-server request done
output = STRPRINTF(
- "Login-server has been asked to unblock the player '%s'."_fmt,
+ "Login-server has been asked to unblock '%s'."_fmt,
player_name);
break;
//case 1: // player not found
case 2: // gm level too low
output = STRPRINTF(
- "Your GM level don't authorise you to unblock the player '%s'."_fmt,
+ "Your GM level doesn't authorize you to unblock '%s'."_fmt,
player_name);
break;
case 3: // login-server offline
output = STRPRINTF(
- "Login-server is offline. Impossible to unblock the the player '%s'."_fmt,
+ "Login-server is offline, so it's impossible to unblock '%s'."_fmt,
player_name);
break;
}
@@ -558,20 +486,20 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed)
case 4: // unban
switch (fixed.error)
{
- case 0: // login-server resquest done
+ case 0: // login-server request done
output = STRPRINTF(
- "Login-server has been asked to unban the player '%s'."_fmt,
+ "Login-server has been asked to unban '%s'."_fmt,
player_name);
break;
//case 1: // player not found
case 2: // gm level too low
output = STRPRINTF(
- "Your GM level don't authorise you to unban the player '%s'."_fmt,
+ "Your GM level doesn't authorize you to unban '%s'."_fmt,
player_name);
break;
case 3: // login-server offline
output = STRPRINTF(
- "Login-server is offline. Impossible to unban the the player '%s'."_fmt,
+ "Login-server is offline, so it's impossible to unban '%s'."_fmt,
player_name);
break;
}
@@ -579,20 +507,20 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed)
case 5: // changesex
switch (fixed.error)
{
- case 0: // login-server resquest done
+ case 0: // login-server request done
output = STRPRINTF(
- "Login-server has been asked to change the sex of the player '%s'."_fmt,
+ "Login-server has been asked to change the sex of '%s'."_fmt,
player_name);
break;
//case 1: // player not found
case 2: // gm level too low
output = STRPRINTF(
- "Your GM level don't authorise you to change the sex of the player '%s'."_fmt,
+ "Your GM level doesn't authorize you to change the sex of '%s'."_fmt,
player_name);
break;
case 3: // login-server offline
output = STRPRINTF(
- "Login-server is offline. Impossible to change the sex of the the player '%s'."_fmt,
+ "Login-server is offline, so it's impossible to change the sex of '%s'."_fmt,
player_name);
break;
}
@@ -603,36 +531,12 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed)
clif_displaymessage(sd->sess, output);
}
else
- PRINTF("chrif_char_ask_name_answer failed - player not online.\n"_fmt);
+ PRINTF("chrif_char_ask_name_answer failed because the player is not online.\n"_fmt);
return 0;
}
/*==========================================
- * End of GM change(@GM) (modified by Yor)
- *------------------------------------------
- */
-static
-void chrif_changedgm(Session *, const Packet_Fixed<0x2b0b>& fixed)
-{
- AccountId acc = fixed.account_id;
- GmLevel level = fixed.gm_level;
-
- dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(acc));
-
- if (battle_config.etc_log)
- PRINTF("chrif_changedgm: account: %d, GM level 0 -> %d.\n"_fmt, acc,
- level);
- if (sd != nullptr)
- {
- if (level)
- clif_displaymessage(sd->sess, "GM modification success."_s);
- else
- clif_displaymessage(sd->sess, "Failure of GM modification."_s);
- }
-}
-
-/*==========================================
* 性別変化終了 (modified by Yor)
*------------------------------------------
*/
@@ -659,15 +563,14 @@ void chrif_changedsex(Session *, const Packet_Fixed<0x2b0d>& fixed)
{
if (sd->status.inventory[i].nameid
&& bool(sd->status.inventory[i].equip))
- pc_unequipitem(sd, i, CalcStatus::NOW);
+ pc_unequipitem(sd, i, CalcStatus::LATER);
}
+ pc_calcstatus(sd, 0);
// save character
chrif_save(sd);
sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
// do same modify in login-server for the account, but no in char-server (it ask again login_id1 to login, and don't remember it)
- clif_displaymessage(sd->sess,
- "Your sex has been changed (need disconexion by the server)..."_s);
- clif_setwaitclose(sd->sess); // forced to disconnect for the change
+ clif_fixpcpos(sd); // use clif_set0078_main_1d8 to send new sex to the client
}
}
else
@@ -750,18 +653,13 @@ int chrif_divorce(CharId char_id, CharId partner_id)
if (sd && sd->status.partner_id == partner_id)
{
sd->status.partner_id = CharId();
-
- if (sd->npc_flags.divorce)
- {
- sd->npc_flags.divorce = 0;
- map_scriptcont(sd, sd->npc_id);
- }
}
sd = map_nick2sd(map_charid2nick(partner_id));
- nullpo_retz(sd);
- if (sd->status.partner_id == char_id)
+ if (sd && sd->status.partner_id == char_id)
+ {
sd->status.partner_id = CharId();
+ }
return 0;
}
@@ -801,14 +699,14 @@ int chrif_accountdeletion(Session *, const Packet_Fixed<0x2b13>& fixed)
{
sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
clif_displaymessage(sd->sess,
- "Your account has been deleted (disconnection)..."_s);
+ "Your account has been deleted. You will now be disconnected..."_s);
clif_setwaitclose(sd->sess); // forced to disconnect for the change
}
}
else
{
if (sd != nullptr)
- PRINTF("chrif_accountdeletion failed - player not online.\n"_fmt);
+ PRINTF("chrif_accountdeletion failed because the player is not online.\n"_fmt);
}
return 0;
@@ -838,11 +736,11 @@ int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed)
{ // status or final date of a banishment
case 1: // 0 = Unregistered ID
clif_displaymessage(sd->sess,
- "Your account has 'Unregistered'."_s);
+ "Your account has an unregistered ID."_s);
break;
case 2: // 1 = Incorrect Password
clif_displaymessage(sd->sess,
- "Your account has an 'Incorrect Password'..."_s);
+ "Your password is incorrect."_s);
break;
case 3: // 2 = This ID is expired
clif_displaymessage(sd->sess,
@@ -850,7 +748,7 @@ int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed)
break;
case 4: // 3 = Rejected from Server
clif_displaymessage(sd->sess,
- "Your account has been rejected from server."_s);
+ "Your account has been rejected by the server."_s);
break;
case 5: // 4 = You have been blocked by the GM Team
clif_displaymessage(sd->sess,
@@ -858,19 +756,19 @@ int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed)
break;
case 6: // 5 = Your Game's EXE file is not the latest version
clif_displaymessage(sd->sess,
- "Your Game's EXE file is not the latest version."_s);
+ "You need to update your client."_s);
break;
case 7: // 6 = Your are Prohibited to log in until %s
clif_displaymessage(sd->sess,
- "Your account has been prohibited to log in."_s);
+ "Your account has been prohibited from logging in."_s);
break;
case 8: // 7 = Server is jammed due to over populated
clif_displaymessage(sd->sess,
- "Server is jammed due to over populated."_s);
+ "The server is overpopulated."_s);
break;
case 9: // 8 = No MSG (actually, all states after 9 except 99 are No MSG, use only this)
clif_displaymessage(sd->sess,
- "Your account has not more authorised."_s);
+ "Your account must be authorized."_s);
break;
case 100: // 99 = This ID has been totally erased
clif_displaymessage(sd->sess,
@@ -878,7 +776,7 @@ int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed)
break;
default:
clif_displaymessage(sd->sess,
- "Your account has not more authorised."_s);
+ "Your account must be authorized."_s);
break;
}
}
@@ -897,7 +795,7 @@ int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed)
else
{
if (sd != nullptr)
- PRINTF("chrif_accountban failed - player not online.\n"_fmt);
+ PRINTF("chrif_accountban failed because the player is not online.\n"_fmt);
}
return 0;
@@ -910,137 +808,17 @@ int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed)
static
int chrif_recvgmaccounts(Session *s, const std::vector<Packet_Repeat<0x2b15>>& repeat)
{
- PRINTF("From login-server: receiving of %d GM accounts information.\n"_fmt,
+ PRINTF("Receiving information on %d GM accounts from login-server.\n"_fmt,
pc_read_gm_account(s, repeat));
return 0;
}
-/*==========================================
- * Request to reload GM accounts and their levels: send to char-server by [Yor]
- *------------------------------------------
- */
-int chrif_reloadGMdb(void)
-{
- if (!char_session)
- return -1;
-
- Packet_Fixed<0x2af7> fixed_f7;
- send_fpacket<0x2af7, 2>(char_session, fixed_f7);
-
- return 0;
-}
-
-/*========================================
- * Map item IDs
- *----------------------------------------
- */
-
-static
-void ladmin_itemfrob_fix_item(ItemNameId source, ItemNameId dest, Item *item)
-{
- if (item && item->nameid == source)
- {
- item->nameid = dest;
- item->equip = EPOS::ZERO;
- }
-}
-
-static
-void ladmin_itemfrob_c2(dumb_ptr<block_list> bl, ItemNameId source_id, ItemNameId dest_id)
-{
-#define IFIX(v) if (v == source_id) {v = dest_id; }
-#define FIX(item) ladmin_itemfrob_fix_item(source_id, dest_id, &item)
-
- if (!bl)
- return;
-
- switch (bl->bl_type)
- {
- case BL::PC:
- {
- dumb_ptr<map_session_data> pc = bl->is_player();
- Storage *stor = account2storage2(pc->status_key.account_id);
-
- for (IOff0 j : IOff0::iter())
- IFIX(pc->status.inventory[j].nameid);
- // cart is no longer supported
- // IFIX(pc->status.weapon);
- IFIX(pc->status.shield);
- IFIX(pc->status.head_top);
- IFIX(pc->status.head_mid);
- IFIX(pc->status.head_bottom);
-
- if (stor)
- {
- for (SOff0 j : SOff0::iter())
- FIX(stor->storage_[j]);
- }
-
- for (IOff0 j : IOff0::iter())
- {
- struct item_data *item = pc->inventory_data[j];
- if (item && item->nameid == source_id)
- {
- item->nameid = dest_id;
- if (bool(item->equip))
- pc_unequipitem(pc, j, CalcStatus::NOW);
- item->nameid = dest_id;
- }
- }
-
- break;
- }
-
- case BL::MOB:
- {
- dumb_ptr<mob_data> mob = bl->is_mob();
- for (Item& itm : mob->lootitemv)
- FIX(itm);
- break;
- }
-
- case BL::ITEM:
- {
- dumb_ptr<flooritem_data> item = bl->is_item();
- FIX(item->item_data);
- break;
- }
- }
-#undef FIX
-#undef IFIX
-}
-
-static
-void ladmin_itemfrob_c(dumb_ptr<block_list> bl, ItemNameId source_id, ItemNameId dest_id)
-{
- ladmin_itemfrob_c2(bl, source_id, dest_id);
-}
-
-static
-void ladmin_itemfrob(Session *, const Packet_Fixed<0x2afa>& fixed)
-{
- ItemNameId source_id = fixed.source_item_id;
- ItemNameId dest_id = fixed.dest_item_id;
- dumb_ptr<block_list> bl = map_get_first_session();
-
- // flooritems
- map_foreachobject(std::bind(ladmin_itemfrob_c, ph::_1, source_id, dest_id),
- BL::NUL /* any object */);
-
- // player characters (and, hopefully, mobs)
- while (bl->bl_next)
- {
- ladmin_itemfrob_c2(bl, source_id, dest_id);
- bl = bl->bl_next;
- }
-}
-
static
void chrif_delete(Session *s)
{
assert (s == char_session);
- PRINTF("Map-server can't connect to char-server (connection #%d).\n"_fmt,
+ PRINTF("map-server can't connect to char-server (connection #%d).\n"_fmt,
s);
char_session = nullptr;
}
@@ -1070,16 +848,6 @@ void chrif_parse(Session *s)
chrif_connectack(s, fixed);
break;
}
- case 0x2afa:
- {
- Packet_Fixed<0x2afa> fixed;
- rv = recv_fpacket<0x2afa, 10>(s, fixed);
- if (rv != RecvResult::Complete)
- break;
-
- ladmin_itemfrob(s, fixed);
- break;
- }
case 0x2afb:
{
Packet_Fixed<0x2afb> fixed;
@@ -1099,12 +867,11 @@ void chrif_parse(Session *s)
AccountId id = payload.account_id;
int login_id2 = payload.login_id2;
- TimeT connect_until_time = payload.connect_until;
short tmw_version = payload.packet_tmw_version;
CharKey st_key = payload.char_key;
CharData st_data = payload.char_data;
pc_authok(id, login_id2,
- connect_until_time, tmw_version,
+ tmw_version,
&st_key, &st_data);
break;
}
@@ -1159,16 +926,6 @@ void chrif_parse(Session *s)
chrif_changemapserverack(s, fixed);
break;
}
- case 0x2b0b:
- {
- Packet_Fixed<0x2b0b> fixed;
- rv = recv_fpacket<0x2b0b, 10>(s, fixed);
- if (rv != RecvResult::Complete)
- break;
-
- chrif_changedgm(s, fixed);
- break;
- }
case 0x2b0d:
{
Packet_Fixed<0x2b0d> fixed;
@@ -1250,7 +1007,7 @@ void chrif_parse(Session *s)
return;
if (battle_config.error_log)
- PRINTF("chrif_parse : unknown packet %d %d\n"_fmt, s,
+ PRINTF("chrif_parse: unknown packet %d %d\n"_fmt, s,
packet_id);
s->set_eof();
return;
@@ -1283,7 +1040,7 @@ void send_users_tochar(TimerData *, tick_t)
if (sd && sd->state.auth &&
!((battle_config.hide_GM_session
|| sd->state.shroud_active
- || bool(sd->status.option & Option::HIDE)) && pc_isGM(sd)))
+ || bool(sd->status.option & Opt0::HIDE)) && pc_isGM(sd)))
{
Packet_Repeat<0x2aff> info;
info.char_id = sd->status_key.char_id;
@@ -1304,9 +1061,9 @@ void check_connect_char_server(TimerData *, tick_t)
{
if (!char_session)
{
- PRINTF("Attempt to connect to char-server...\n"_fmt);
+ PRINTF("Attempting to connect to char-server...\n"_fmt);
chrif_state = 0;
- char_session = make_connection(char_ip, char_port,
+ char_session = make_connection(map_conf.char_ip, map_conf.char_port,
SessionParsers{.func_parse= chrif_parse, .func_delete= chrif_delete});
if (!char_session)
return;
@@ -1331,4 +1088,5 @@ void do_init_chrif(void)
5_s
).detach();
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/chrif.hpp b/src/map/chrif.hpp
index 4711bc5..655103d 100644
--- a/src/map/chrif.hpp
+++ b/src/map/chrif.hpp
@@ -22,24 +22,11 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
-#include "../generic/fwd.hpp"
-
-#include "../net/fwd.hpp"
-
-#include "../mmo/fwd.hpp"
-
namespace tmwa
{
-void chrif_setuserid(AccountName);
-void chrif_setpasswd(AccountPass);
-AccountPass chrif_getpasswd(void);
-
-void chrif_setip(IP4Address);
-void chrif_setport(int);
-
+namespace map
+{
int chrif_isconnect(void);
int chrif_authreq(dumb_ptr<map_session_data>);
@@ -55,12 +42,8 @@ void chrif_changeemail(AccountId id, AccountEmail actual_email, AccountEmail new
void chrif_char_ask_name(AccountId id, CharName character_name, short operation_type,
HumanTimeDiff modif);
int chrif_saveaccountreg2(dumb_ptr<map_session_data> sd);
-int chrif_reloadGMdb(void);
int chrif_send_divorce(CharId char_id);
void do_init_chrif(void);
-
-// only used by intif.cpp
-// and clif.cpp for the new on_delete stuff ...
-extern Session *char_session;
+} // namespace map
} // namespace tmwa
diff --git a/src/map/clif.cpp b/src/map/clif.cpp
index b47bf28..243ffaf 100644
--- a/src/map/clif.cpp
+++ b/src/map/clif.cpp
@@ -36,30 +36,36 @@
#include "../strings/xstring.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
+#include "../io/extract.hpp"
#include "../io/write.hpp"
#include "../net/ip.hpp"
-#include "../net/packets.hpp"
#include "../net/socket.hpp"
#include "../net/timer.hpp"
+#include "../net/timestamp-utils.hpp"
#include "../proto2/any-user.hpp"
#include "../proto2/char-map.hpp"
#include "../proto2/map-user.hpp"
-#include "../mmo/md5more.hpp"
-#include "../mmo/utils.hpp"
+#include "../mmo/cxxstdio_enums.hpp"
#include "../mmo/version.hpp"
+#include "../high/md5more.hpp"
+
+#include "../wire/packets.hpp"
+
#include "atcommand.hpp"
#include "battle.hpp"
+#include "battle_conf.hpp"
#include "chrif.hpp"
+#include "globals.hpp"
#include "intif.hpp"
#include "itemdb.hpp"
#include "magic.hpp"
#include "magic-stmt.hpp"
#include "map.hpp"
+#include "map_conf.hpp"
#include "npc.hpp"
#include "party.hpp"
#include "pc.hpp"
@@ -73,6 +79,8 @@
namespace tmwa
{
+namespace map
+{
constexpr int EMOTE_IGNORED = 0x0e;
// functions list. Rate is how many milliseconds are required between
@@ -117,11 +125,6 @@ enum class SendWho
};
static
-IP4Address map_ip;
-static
-int map_port = 5121;
-
-static
int clif_changelook_towards(dumb_ptr<block_list> bl, LOOK type, int val,
dumb_ptr<map_session_data> dstsd);
static
@@ -149,42 +152,6 @@ void clif_delete(Session *s)
/*==========================================
- * map鯖のip設定
- *------------------------------------------
- */
-void clif_setip(IP4Address ip)
-{
- map_ip = ip;
-}
-
-/*==========================================
- * map鯖のport設定
- *------------------------------------------
- */
-void clif_setport(int port)
-{
- map_port = port;
-}
-
-/*==========================================
- * map鯖のip読み出し
- *------------------------------------------
- */
-IP4Address clif_getip(void)
-{
- return map_ip;
-}
-
-/*==========================================
- * map鯖のport読み出し
- *------------------------------------------
- */
-int clif_getport(void)
-{
- return map_port;
-}
-
-/*==========================================
*
*------------------------------------------
*/
@@ -231,11 +198,6 @@ int is_deaf(dumb_ptr<block_list> bl)
return sd->special_state.deaf;
}
-static
-void clif_emotion_towards(dumb_ptr<block_list> bl,
- dumb_ptr<block_list> target, int type);
-
-
enum class ChatType
{
Party,
@@ -295,7 +257,6 @@ void clif_send_sub(dumb_ptr<block_list> bl, const Buffer& buf,
static
int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type)
{
- PartyPair p;
int x0 = 0, x1 = 0, y0 = 0, y1 = 0;
if (type != SendWho::ALL_CLIENT)
@@ -305,7 +266,7 @@ int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type)
if (bl->bl_type == BL::PC)
{
dumb_ptr<map_session_data> sd2 = bl->is_player();
- if (bool(sd2->status.option & Option::INVISIBILITY))
+ if (bool(sd2->status.option & Opt0::INVISIBILITY))
{
// Obscure hidden GMs
@@ -384,20 +345,22 @@ int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type)
case SendWho::PARTY_WOS: // 自分以外の全パーティーメンバに送信
case SendWho::PARTY_SAMEMAP: // 同じマップの全パーティーメンバに送信
case SendWho::PARTY_SAMEMAP_WOS: // 自分以外の同じマップの全パーティーメンバに送信
+ {
+ Option<PartyPair> p_ = None;
if (bl->bl_type == BL::PC)
{
dumb_ptr<map_session_data> sd = bl->is_player();
if (sd->partyspy)
{
- p = party_search(sd->partyspy);
+ p_ = party_search(sd->partyspy);
}
else
{
if (sd->status.party_id)
- p = party_search(sd->status.party_id);
+ p_ = party_search(sd->status.party_id);
}
}
- if (p)
+ OMATCH_BEGIN_SOME (p, p_)
{
for (int i = 0; i < MAX_PARTY; i++)
{
@@ -436,6 +399,8 @@ int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type)
}
}
}
+ OMATCH_END ();
+ }
break;
case SendWho::SELF:
{
@@ -617,37 +582,6 @@ int clif_clearchar(dumb_ptr<block_list> bl, BeingRemoveWhy type)
return 0;
}
-static
-void clif_clearchar_delay_sub(TimerData *, tick_t,
- dumb_ptr<block_list> bl, BeingRemoveWhy type)
-{
- clif_clearchar(bl, type);
- MapBlockLock::freeblock(bl);
-}
-
-int clif_clearchar_delay(tick_t tick,
- dumb_ptr<block_list> bl, BeingRemoveWhy type)
-{
- dumb_ptr<block_list> tmpbl;
- tmpbl.new_();
-
- // yikes!
- tmpbl->bl_next = bl->bl_next;
- tmpbl->bl_prev = bl->bl_prev;
- tmpbl->bl_id = bl->bl_id;
- tmpbl->bl_m = bl->bl_m;
- tmpbl->bl_x = bl->bl_x;
- tmpbl->bl_y = bl->bl_y;
- tmpbl->bl_type = bl->bl_type;
-
- Timer(tick,
- std::bind(clif_clearchar_delay_sub, ph::_1, ph::_2,
- tmpbl, type)
- ).detach();
-
- return 0;
-}
-
/*==========================================
*
*------------------------------------------
@@ -684,14 +618,14 @@ void clif_set0078_main_1d8(dumb_ptr<map_session_data> sd, Buffer& buf)
fixed_1d8.weapon = sd->attack_spell_look_override;
else
{
- if (widx.ok() && sd->inventory_data[widx])
+ if (widx.ok() && sd->inventory_data[widx].is_some())
{
fixed_1d8.weapon = sd->status.inventory[widx].nameid;
}
else
fixed_1d8.weapon = ItemNameId();
}
- if (sidx.ok() && sidx != widx && sd->inventory_data[sidx])
+ if (sidx.ok() && sidx != widx && sd->inventory_data[sidx].is_some())
{
fixed_1d8.shield = sd->status.inventory[sidx].nameid;
}
@@ -738,14 +672,14 @@ void clif_set0078_alt_1d9(dumb_ptr<map_session_data> sd, Buffer& buf)
fixed_1d8.weapon = sd->attack_spell_look_override;
else
{
- if (widx.ok() && sd->inventory_data[widx])
+ if (widx.ok() && sd->inventory_data[widx].is_some())
{
fixed_1d8.weapon = sd->status.inventory[widx].nameid;
}
else
fixed_1d8.weapon = ItemNameId();
}
- if (sidx.ok() && sidx != widx && sd->inventory_data[sidx])
+ if (sidx.ok() && sidx != widx && sd->inventory_data[sidx].is_some())
{
fixed_1d8.shield = sd->status.inventory[sidx].nameid;
}
@@ -791,13 +725,13 @@ void clif_set007b(dumb_ptr<map_session_data> sd, Buffer& buf)
fixed_1da.hair_style = sd->status.hair;
IOff0 widx = sd->equip_index_maybe[EQUIP::WEAPON];
IOff0 sidx = sd->equip_index_maybe[EQUIP::SHIELD];
- if (widx.ok() && sd->inventory_data[widx])
+ if (widx.ok() && sd->inventory_data[widx].is_some())
{
fixed_1da.weapon = sd->status.inventory[widx].nameid;
}
else
fixed_1da.weapon = ItemNameId();
- if (sidx.ok() && sidx != widx && sd->inventory_data[sidx])
+ if (sidx.ok() && sidx != widx && sd->inventory_data[sidx].is_some())
{
fixed_1da.shield = sd->status.inventory[sidx].nameid;
}
@@ -969,7 +903,7 @@ int clif_spawnnpc(dumb_ptr<npc_data> nd)
{
nullpo_retz(nd);
- if (nd->npc_class == NEGATIVE_SPECIES || nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS)
+ if (nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS)
return 0;
Packet_Fixed<0x007c> fixed_7c;
@@ -1002,7 +936,7 @@ int clif_spawn_fake_npc_for_player(dumb_ptr<map_session_data> sd, BlockId fake_n
fixed_7c.speed = interval_t();
fixed_7c.opt1 = Opt1::ZERO;
fixed_7c.opt2 = Opt2::ZERO;
- fixed_7c.option = Option::ZERO;
+ fixed_7c.option = Opt0::ZERO;
fixed_7c.species = FAKE_NPC_CLASS;
fixed_7c.pos.x = sd->bl_x;
fixed_7c.pos.y = sd->bl_y;
@@ -1013,7 +947,7 @@ int clif_spawn_fake_npc_for_player(dumb_ptr<map_session_data> sd, BlockId fake_n
fixed_78.speed = interval_t();
fixed_78.opt1 = Opt1::ZERO;
fixed_78.opt2 = Opt2::ZERO;
- fixed_78.option = Option::ZERO;
+ fixed_78.option = Opt0::ZERO;
fixed_78.species = FAKE_NPC_CLASS;
fixed_78.unused_head_bottom_or_species_again = unwrap<Species>(FAKE_NPC_CLASS);
fixed_78.pos.x = sd->bl_x;
@@ -1224,7 +1158,6 @@ int clif_npcbuysell(dumb_ptr<map_session_data> sd, BlockId id)
*/
int clif_buylist(dumb_ptr<map_session_data> sd, dumb_ptr<npc_data_shop> nd)
{
- struct item_data *id;
int i, val;
nullpo_retz(sd);
@@ -1234,7 +1167,7 @@ int clif_buylist(dumb_ptr<map_session_data> sd, dumb_ptr<npc_data_shop> nd)
std::vector<Packet_Repeat<0x00c6>> repeat_c6(nd->shop_items.size());
for (i = 0; i < nd->shop_items.size(); i++)
{
- id = itemdb_search(nd->shop_items[i].nameid);
+ P<struct item_data> id = itemdb_search(nd->shop_items[i].nameid);
val = nd->shop_items[i].value;
repeat_c6[i].base_price = val; // base price
repeat_c6[i].actual_price = val; // actual price
@@ -1258,9 +1191,11 @@ int clif_selllist(dumb_ptr<map_session_data> sd)
std::vector<Packet_Repeat<0x00c7>> repeat_c7;
for (IOff0 i : IOff0::iter())
{
- if (sd->status.inventory[i].nameid && sd->inventory_data[i])
+ if (!sd->status.inventory[i].nameid)
+ continue;
+ OMATCH_BEGIN_SOME (sdidi, sd->inventory_data[i])
{
- int val = sd->inventory_data[i]->value_sell;
+ int val = sdidi->value_sell;
if (val < 0)
continue;
Packet_Repeat<0x00c7> info;
@@ -1269,6 +1204,7 @@ int clif_selllist(dumb_ptr<map_session_data> sd)
info.actual_price = val;
repeat_c7.push_back(info);
}
+ OMATCH_END ();
}
send_packet_repeatonly<0x00c7, 4, 10>(s, repeat_c7);
@@ -1379,9 +1315,9 @@ int clif_additem(dumb_ptr<map_session_data> sd, IOff0 n, int amount, PickupFail
}
else
{
- if (!n.ok() || !sd->status.inventory[n].nameid
- || sd->inventory_data[n] == nullptr)
+ if (!n.ok() || !sd->status.inventory[n].nameid)
return 1;
+ auto sdidn = TRY_UNWRAP(sd->inventory_data[n], return 1);
fixed_a0.ioff2 = n.shift();
fixed_a0.amount = amount;
@@ -1396,9 +1332,9 @@ int clif_additem(dumb_ptr<map_session_data> sd, IOff0 n, int amount, PickupFail
fixed_a0.card3 = 0;
}
fixed_a0.epos = pc_equippoint(sd, n);
- fixed_a0.item_type = (sd->inventory_data[n]->type == ItemType::_7
+ fixed_a0.item_type = (sdidn->type == ItemType::_7
? ItemType::WEAPON
- : sd->inventory_data[n]->type);
+ : sdidn->type);
fixed_a0.pickup_fail = fail;
}
@@ -1435,17 +1371,18 @@ void clif_itemlist(dumb_ptr<map_session_data> sd)
std::vector<Packet_Repeat<0x01ee>> repeat_1ee;
for (IOff0 i : IOff0::iter())
{
- if (!sd->status.inventory[i].nameid
- || sd->inventory_data[i] == nullptr
- || itemdb_isequip2(sd->inventory_data[i]))
+ if (!sd->status.inventory[i].nameid)
+ continue;
+ auto sdidi = TRY_UNWRAP(sd->inventory_data[i], continue);
+ if (itemdb_isequip2(sdidi))
continue;
Packet_Repeat<0x01ee> info;
info.ioff2 = i.shift();
info.name_id = sd->status.inventory[i].nameid;
- info.item_type = sd->inventory_data[i]->type;
+ info.item_type = sdidi->type;
info.identify = 1;
info.amount = sd->status.inventory[i].amount;
- if (sd->inventory_data[i]->equip == EPOS::ARROW)
+ if (sdidi->equip == EPOS::ARROW)
{
info.epos = EPOS::ARROW;
if (bool(sd->status.inventory[i].equip))
@@ -1479,17 +1416,18 @@ void clif_equiplist(dumb_ptr<map_session_data> sd)
std::vector<Packet_Repeat<0x00a4>> repeat_a4;
for (IOff0 i : IOff0::iter())
{
- if (!sd->status.inventory[i].nameid
- || sd->inventory_data[i] == nullptr
- || !itemdb_isequip2(sd->inventory_data[i]))
+ if (!sd->status.inventory[i].nameid)
+ continue;
+ P<struct item_data> sdidi = TRY_UNWRAP(sd->inventory_data[i], continue);
+ if (!itemdb_isequip2(sdidi))
continue;
Packet_Repeat<0x00a4> info;
info.ioff2 = i.shift();
info.name_id = sd->status.inventory[i].nameid;
info.item_type = (
- sd->inventory_data[i]->type == ItemType::_7
+ sdidi->type == ItemType::_7
? ItemType::WEAPON
- : sd->inventory_data[i]->type);
+ : sdidi->type);
info.identify = 0;
info.epos_pc = pc_equippoint(sd, i);
info.epos_inv = sd->status.inventory[i].equip;
@@ -1513,10 +1451,9 @@ void clif_equiplist(dumb_ptr<map_session_data> sd)
* カプラさんに預けてある消耗品&収集品リスト
*------------------------------------------
*/
-int clif_storageitemlist(dumb_ptr<map_session_data> sd, Storage *stor)
+int clif_storageitemlist(dumb_ptr<map_session_data> sd, Borrowed<Storage> stor)
{
nullpo_retz(sd);
- nullpo_retz(stor);
Session *s = sd->sess;
std::vector<Packet_Repeat<0x01f0>> repeat_1f0;
@@ -1525,9 +1462,7 @@ int clif_storageitemlist(dumb_ptr<map_session_data> sd, Storage *stor)
if (!stor->storage_[i].nameid)
continue;
- struct item_data *id;
- id = itemdb_search(stor->storage_[i].nameid);
- nullpo_retz(id);
+ P<struct item_data> id = itemdb_search(stor->storage_[i].nameid);
if (itemdb_isequip2(id))
continue;
@@ -1555,10 +1490,9 @@ int clif_storageitemlist(dumb_ptr<map_session_data> sd, Storage *stor)
* カプラさんに預けてある装備リスト
*------------------------------------------
*/
-int clif_storageequiplist(dumb_ptr<map_session_data> sd, Storage *stor)
+int clif_storageequiplist(dumb_ptr<map_session_data> sd, Borrowed<Storage> stor)
{
nullpo_retz(sd);
- nullpo_retz(stor);
Session *s = sd->sess;
std::vector<Packet_Repeat<0x00a6>> repeat_a6;
@@ -1567,9 +1501,7 @@ int clif_storageequiplist(dumb_ptr<map_session_data> sd, Storage *stor)
if (!stor->storage_[i].nameid)
continue;
- struct item_data *id;
- id = itemdb_search(stor->storage_[i].nameid);
- nullpo_retz(id);
+ P<struct item_data> id = itemdb_search(stor->storage_[i].nameid);
if (!itemdb_isequip2(id))
continue;
Packet_Repeat<0x00a6> info;
@@ -1831,7 +1763,7 @@ int clif_changelook_towards(dumb_ptr<block_list> bl, LOOK type, int val,
if (bl->bl_type == BL::PC)
sd = bl->is_player();
- if (sd && bool(sd->status.option & Option::INVISIBILITY))
+ if (sd && bool(sd->status.option & Opt0::INVISIBILITY))
return 0;
if (sd
@@ -1845,7 +1777,7 @@ int clif_changelook_towards(dumb_ptr<block_list> bl, LOOK type, int val,
fixed_1d7.look_type = type;
IOff0 idx = sd->equip_index_maybe[equip_point];
- if (idx.ok() && sd->inventory_data[idx])
+ if (idx.ok() && sd->inventory_data[idx].is_some())
{
fixed_1d7.weapon_or_name_id_or_value = unwrap<ItemNameId>(sd->status.inventory[idx].nameid);
}
@@ -1862,14 +1794,14 @@ int clif_changelook_towards(dumb_ptr<block_list> bl, LOOK type, int val,
fixed_1d7.weapon_or_name_id_or_value = unwrap<ItemNameId>(sd->attack_spell_look_override);
else
{
- if (widx.ok() && sd->inventory_data[widx])
+ if (widx.ok() && sd->inventory_data[widx].is_some())
{
fixed_1d7.weapon_or_name_id_or_value = unwrap<ItemNameId>(sd->status.inventory[widx].nameid);
}
else
fixed_1d7.weapon_or_name_id_or_value = unwrap<ItemNameId>(ItemNameId());
}
- if (sidx.ok() && sidx != widx && sd->inventory_data[sidx])
+ if (sidx.ok() && sidx != widx && sd->inventory_data[sidx].is_some())
{
fixed_1d7.shield = sd->status.inventory[sidx].nameid;
}
@@ -2074,7 +2006,7 @@ int clif_changeoption(dumb_ptr<block_list> bl)
nullpo_retz(bl);
- Option option = *battle_get_option(bl);
+ Opt0 option = *battle_get_option(bl);
sc_data = battle_get_sc_data(bl);
Packet_Fixed<0x0119> fixed_119;
@@ -2263,10 +2195,9 @@ int clif_tradecompleted(dumb_ptr<map_session_data> sd, int fail)
*------------------------------------------
*/
int clif_updatestorageamount(dumb_ptr<map_session_data> sd,
- Storage *stor)
+ Borrowed<Storage> stor)
{
nullpo_retz(sd);
- nullpo_retz(stor);
Session *s = sd->sess;
Packet_Fixed<0x00f2> fixed_f2;
@@ -2281,11 +2212,10 @@ int clif_updatestorageamount(dumb_ptr<map_session_data> sd,
* カプラ倉庫にアイテムを追加する
*------------------------------------------
*/
-int clif_storageitemadded(dumb_ptr<map_session_data> sd, Storage *stor,
+int clif_storageitemadded(dumb_ptr<map_session_data> sd, Borrowed<Storage> stor,
SOff0 index, int amount)
{
nullpo_retz(sd);
- nullpo_retz(stor);
Session *s = sd->sess;
Packet_Fixed<0x00f4> fixed_f4;
@@ -2357,7 +2287,7 @@ static
void clif_getareachar_pc(dumb_ptr<map_session_data> sd,
dumb_ptr<map_session_data> dstsd)
{
- if (bool(dstsd->status.option & Option::INVISIBILITY))
+ if (bool(dstsd->status.option & Opt0::INVISIBILITY))
return;
nullpo_retv(sd);
@@ -2392,7 +2322,7 @@ void clif_getareachar_npc(dumb_ptr<map_session_data> sd, dumb_ptr<npc_data> nd)
nullpo_retv(sd);
nullpo_retv(nd);
- if (nd->npc_class == NEGATIVE_SPECIES || nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS)
+ if (nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS)
return;
Buffer buf;
@@ -2470,7 +2400,7 @@ int clif_fixpcpos(dumb_ptr<map_session_data> sd)
*/
int clif_damage(dumb_ptr<block_list> src, dumb_ptr<block_list> dst,
tick_t tick, interval_t sdelay, interval_t ddelay, int damage,
- int div, DamageType type, int damage2)
+ int div, DamageType type)
{
eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
@@ -2488,7 +2418,7 @@ int clif_damage(dumb_ptr<block_list> src, dumb_ptr<block_list> dst,
fixed_8a.damage = (damage > 0x7fff) ? 0x7fff : damage;
fixed_8a.div = div;
fixed_8a.damage_type = type;
- fixed_8a.damage2 = damage2;
+ fixed_8a.damage2 = 0;
Buffer buf = create_fpacket<0x008a, 29>(fixed_8a);
clif_send(buf, src, SendWho::AREA);
@@ -2686,43 +2616,6 @@ void clif_mobinsight(dumb_ptr<block_list> bl, dumb_ptr<mob_data> md)
}
/*==========================================
- *
- *------------------------------------------
- */
-int clif_skillinfo(dumb_ptr<map_session_data> sd, SkillID skillid, int type,
- int range)
-{
- nullpo_retz(sd);
-
- Session *s = sd->sess;
- if (!sd->status.skill[skillid].lv)
- return 0;
- Packet_Fixed<0x0147> fixed_147;
- fixed_147.info.skill_id = skillid;
- if (type < 0)
- fixed_147.info.type_or_inf = skill_get_inf(skillid);
- else
- fixed_147.info.type_or_inf = type;
- fixed_147.info.flags = SkillFlags::ZERO;
- fixed_147.info.level = sd->status.skill[skillid].lv;
- fixed_147.info.sp = skill_get_sp(skillid, sd->status.skill[skillid].lv);
- if (range < 0)
- {
- range = skill_get_range(skillid, sd->status.skill[skillid].lv);
- if (range < 0)
- range = battle_get_range(sd) - (range + 1);
- fixed_147.info.range = range;
- }
- else
- fixed_147.info.range = range;
- fixed_147.info.unused = ""_s;
- fixed_147.info.can_raise = sd->status.skill[skillid].lv < skill_get_max_raise(skillid);
- send_fpacket<0x0147, 39>(s, fixed_147);
-
- return 0;
-}
-
-/*==========================================
* スキルリストを送信する
*------------------------------------------
*/
@@ -2971,8 +2864,6 @@ int clif_party_info(PartyPair p, Session *s)
int i;
dumb_ptr<map_session_data> sd = nullptr;
- nullpo_retz(p);
-
Packet_Head<0x00fb> head_fb;
std::vector<Packet_Repeat<0x00fb>> repeat_fb;
head_fb.party_name = p->name;
@@ -3018,15 +2909,12 @@ int clif_party_info(PartyPair p, Session *s)
void clif_party_invite(dumb_ptr<map_session_data> sd,
dumb_ptr<map_session_data> tsd)
{
- PartyPair p;
-
nullpo_retv(sd);
nullpo_retv(tsd);
Session *s = tsd->sess;
- if (!(p = party_search(sd->status.party_id)))
- return;
+ PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return);
Packet_Fixed<0x00fe> fixed_fe;
fixed_fe.account_id = sd->status_key.account_id;
@@ -3068,8 +2956,6 @@ void clif_party_inviteack(dumb_ptr<map_session_data> sd, CharName nick, int flag
*/
void clif_party_option(PartyPair p, dumb_ptr<map_session_data> sd, int flag)
{
- nullpo_retv(p);
-
if (sd == nullptr && flag == 0)
{
int i;
@@ -3102,8 +2988,6 @@ void clif_party_leaved(PartyPair p, dumb_ptr<map_session_data> sd,
{
int i;
- nullpo_retv(p);
-
Packet_Fixed<0x0105> fixed_105;
fixed_105.account_id = account_id;
fixed_105.char_name = name;
@@ -3140,8 +3024,6 @@ void clif_party_message(PartyPair p, AccountId account_id, XString mes)
dumb_ptr<map_session_data> sd = nullptr;
int i;
- nullpo_retv(p);
-
for (i = 0; i < MAX_PARTY; i++)
{
sd = dumb_ptr<map_session_data>(p->member[i].sd);
@@ -3214,21 +3096,6 @@ int clif_movetoattack(dumb_ptr<map_session_data> sd, dumb_ptr<block_list> bl)
}
/*==========================================
- * MVPエフェクト
- *------------------------------------------
- */
-int clif_mvp_effect(dumb_ptr<map_session_data> sd)
-{
- nullpo_retz(sd);
-
- Packet_Fixed<0x010c> fixed_10c;
- fixed_10c.block_id = sd->bl_id;
- Buffer buf = create_fpacket<0x010c, 6>(fixed_10c);
- clif_send(buf, sd, SendWho::AREA);
- return 0;
-}
-
-/*==========================================
* エモーション
*------------------------------------------
*/
@@ -3243,7 +3110,6 @@ void clif_emotion(dumb_ptr<block_list> bl, int type)
clif_send(buf, bl, SendWho::AREA);
}
-static
void clif_emotion_towards(dumb_ptr<block_list> bl,
dumb_ptr<block_list> target, int type)
{
@@ -3442,9 +3308,9 @@ RecvResult clif_parse_LoadEndAck(Session *s, dumb_ptr<map_session_data> sd)
// 119
// 78
- if (battle_config.player_invincible_time > 0)
+ if (battle_config.player_invincible_time > interval_t::zero())
{
- pc_setinvincibletimer(sd, static_cast<interval_t>(battle_config.player_invincible_time));
+ pc_setinvincibletimer(sd, battle_config.player_invincible_time);
}
map_addblock(sd); // ブロック登録
@@ -3467,7 +3333,6 @@ RecvResult clif_parse_LoadEndAck(Session *s, dumb_ptr<map_session_data> sd)
std::bind(pc_calc_pvprank_timer, ph::_1, ph::_2,
sd->bl_id));
sd->pvp_rank = 0;
- sd->pvp_lastusers = 0;
sd->pvp_point = 5;
}
}
@@ -3642,16 +3507,20 @@ RecvResult clif_parse_GetCharNameRequest(Session *s, dumb_ptr<map_session_data>
fixed_95.char_name = ssd->status_key.name;
send_fpacket<0x0095, 30>(s, fixed_95);
- PartyPair p;
-
PartyName party_name;
int send = 0;
- if (ssd->status.party_id && (p = party_search(ssd->status.party_id)))
+ if (ssd->status.party_id)
{
- party_name = p->name;
- send = 1;
+ Option<PartyPair> p_ = party_search(ssd->status.party_id);
+
+ OMATCH_BEGIN_SOME (p, p_)
+ {
+ party_name = p->name;
+ send = 1;
+ }
+ OMATCH_END ();
}
if (send)
@@ -3665,7 +3534,7 @@ RecvResult clif_parse_GetCharNameRequest(Session *s, dumb_ptr<map_session_data>
send_fpacket<0x0195, 102>(s, fixed_195);
}
- if (pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.hack_info_GM_level))))
+ if (pc_isGM(sd).satisfies(battle_config.hack_info_GM_level))
{
IP4Address ip = ssd->get_ip();
Packet_Fixed<0x020c> fixed_20c;
@@ -3853,25 +3722,6 @@ RecvResult clif_parse_Emotion(Session *s, dumb_ptr<map_session_data> sd)
*------------------------------------------
*/
static
-RecvResult clif_parse_HowManyConnections(Session *s, dumb_ptr<map_session_data>)
-{
- Packet_Fixed<0x00c1> fixed;
- RecvResult rv = recv_fpacket<0x00c1, 2>(s, fixed);
- if (rv != RecvResult::Complete)
- return rv;
-
- Packet_Fixed<0x00c2> fixed_c2;
- fixed_c2.users = map_getusers();
- send_fpacket<0x00c2, 6>(s, fixed_c2);
-
- return rv;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
RecvResult clif_parse_ActionRequest(Session *s, dumb_ptr<map_session_data> sd)
{
Packet_Fixed<0x0089> fixed;
@@ -3904,7 +3754,7 @@ RecvResult clif_parse_ActionRequest(Session *s, dumb_ptr<map_session_data> sd)
{
case DamageType::NORMAL:
case DamageType::CONTINUOUS:
- if (bool(sd->status.option & Option::HIDE))
+ if (bool(sd->status.option & Opt0::HIDE))
return rv;
if (!battle_config.skill_delay_attack_enable)
{
@@ -4046,7 +3896,7 @@ RecvResult clif_parse_Wis(Session *s, dumb_ptr<map_session_data> sd)
if (dstsd->sess == s)
{
ZString mes = "You cannot page yourself."_s;
- clif_wis_message(s, wisp_server_name, mes);
+ clif_wis_message(s, WISP_SERVER_NAME, mes);
}
else
{
@@ -4199,15 +4049,16 @@ RecvResult clif_parse_EquipItem(Session *s, dumb_ptr<map_session_data> sd)
if (sd->npc_id)
return rv;
- if (sd->inventory_data[index])
+ OMATCH_BEGIN_SOME (sdidi, sd->inventory_data[index])
{
EPOS epos = fixed.epos_ignored;
- if (sd->inventory_data[index]->type == ItemType::ARROW)
+ if (sdidi->type == ItemType::ARROW)
epos = EPOS::ARROW;
// Note: the EPOS argument to pc_equipitem is actually ignored
pc_equipitem(sd, index, epos);
}
+ OMATCH_END ();
return rv;
}
@@ -4816,6 +4667,60 @@ RecvResult clif_parse_PartyMessage(Session *s, dumb_ptr<map_session_data> sd)
return rv;
}
+void clif_sendallquest(dumb_ptr<map_session_data> sd)
+{
+ int i;
+ QuestId questid;
+ if (!sd)
+ return;
+
+ if (!sd->sess)
+ return;
+
+ Session *s = sd->sess;
+ Packet_Head<0x0215> head_215;
+ std::vector<Packet_Repeat<0x0215>> repeat_215;
+
+ assert (sd->status.global_reg_num < GLOBAL_REG_NUM);
+ for (QuestId q = wrap<QuestId>(0); q < wrap<QuestId>(-1); q = next(q))
+ {
+ P<struct quest_data> quest_data_ = TRY_UNWRAP(questdb_exists(q), continue);
+ for (i = 0; i < sd->status.global_reg_num; i++)
+ {
+ if (sd->status.global_reg[i].str == quest_data_->quest_vr)
+ {
+ int val = ((sd->status.global_reg[i].value & (((1 << quest_data_->quest_mask) - 1) << (quest_data_->quest_shift * quest_data_->quest_mask))) >> (quest_data_->quest_shift * quest_data_->quest_mask));
+ Packet_Repeat<0x0215> info;
+ info.variable = unwrap<QuestId>(quest_data_->questid);
+ info.value = val;
+ repeat_215.push_back(info);
+ break;
+ }
+ }
+ }
+
+ send_vpacket<0x0215, 4, 6>(s, head_215, repeat_215);
+ return;
+}
+
+void clif_sendquest(dumb_ptr<map_session_data> sd, QuestId questid, int value)
+{
+ if (!sd)
+ return;
+
+ if (!sd->sess)
+ return;
+
+ Session *s = sd->sess;
+
+ Packet_Fixed<0x0214> fixed;
+ fixed.variable = unwrap<QuestId>(questid);
+ fixed.value = value;
+ send_fpacket<0x0214, 8>(s, fixed);
+ return;
+}
+
+
func_table clif_parse_func_table[0x0220] =
{
{0, 10, nullptr, }, // 0x0000
@@ -5011,7 +4916,7 @@ func_table clif_parse_func_table[0x0220] =
{0, 5, nullptr, }, // 0x00be
{1000, 3, clif_parse_Emotion, }, // 0x00bf
{0, 7, nullptr, }, // 0x00c0
- {0, 2, clif_parse_HowManyConnections, }, // 0x00c1
+ {0, 2, nullptr, }, // 0x00c1
{0, 6, nullptr, }, // 0x00c2
{0, 8, nullptr, }, // 0x00c3
{0, 6, nullptr, }, // 0x00c4
@@ -5431,12 +5336,10 @@ uint16_t clif_check_packet_flood(Session *s, int cmd)
// They are flooding
if (tick < sd->flood_rates[cmd] + rate)
{
- TimeT now = TimeT::now();
-
// If it's a nasty flood we log and possibly kick
- if (now > sd->packet_flood_reset_due)
+ if (tick > sd->packet_flood_reset_due)
{
- sd->packet_flood_reset_due = static_cast<time_t>(now) + battle_config.packet_spam_threshold;
+ sd->packet_flood_reset_due = tick + battle_config.packet_spam_threshold;
sd->packet_flood_in = 0;
}
@@ -5638,7 +5541,6 @@ unknown_packet:
PRINTF("\nclif_parse: session #%d, packet 0x%x, lenght %zu\n"_fmt,
s, packet_id, packet_avail(s));
{
- ZString packet_txt = "save/packet.txt"_s;
if (sd && sd->state.auth)
{
PRINTF("Unknown packet: Account ID %d, character ID %d, player name %s.\n"_fmt,
@@ -5650,35 +5552,27 @@ unknown_packet:
else
PRINTF("Unknown packet (unknown)\n"_fmt);
- io::AppendFile fp(packet_txt);
- if (!fp.is_open())
- {
- PRINTF("clif.c: cant write [%s] !!! data is lost !!!\n"_fmt,
- packet_txt);
- return;
- }
- else
{
timestamp_seconds_buffer now;
stamp_time(now);
if (sd && sd->state.auth)
{
- FPRINTF(fp,
+ FPRINTF(stderr,
"%s\nPlayer with account ID %d (character ID %d, player name %s) sent wrong packet:\n"_fmt,
now,
sd->status_key.account_id,
sd->status_key.char_id, sd->status_key.name);
}
else if (sd) // not authentified! (refused by char-server or disconnect before to be authentified)
- FPRINTF(fp,
+ FPRINTF(stderr,
"%s\nUnauthenticated player with account ID %d sent wrong packet:\n"_fmt,
now, sd->bl_id);
else
- FPRINTF(fp,
+ FPRINTF(stderr,
"%s\nUnknown connection sent wrong packet:\n"_fmt,
now);
- packet_dump(fp, s);
+ packet_dump(s);
}
}
}
@@ -5687,6 +5581,7 @@ unknown_packet:
void do_init_clif(void)
{
- make_listen_port(map_port, SessionParsers{.func_parse= clif_parse, .func_delete= clif_delete});
+ make_listen_port(map_conf.map_port, SessionParsers{.func_parse= clif_parse, .func_delete= clif_delete});
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/clif.hpp b/src/map/clif.hpp
index adb4889..5117ff3 100644
--- a/src/map/clif.hpp
+++ b/src/map/clif.hpp
@@ -20,34 +20,26 @@
// 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 "fwd.hpp"
+#include "../mmo/clif.t.hpp"
-#include "clif.t.hpp"
+#include "fwd.hpp"
#include <functional>
-#include "../strings/fwd.hpp"
-
-#include "../generic/fwd.hpp"
+#include "../high/mmo.hpp"
#include "../net/timer.t.hpp"
-#include "../mmo/fwd.hpp"
-#include "../mmo/mmo.hpp"
-
#include "battle.t.hpp"
#include "map.t.hpp"
#include "pc.t.hpp"
-#include "skill.t.hpp"
+#include "../mmo/skill.t.hpp"
namespace tmwa
{
-void clif_setip(IP4Address);
-void clif_setport(int);
-
-IP4Address clif_getip(void);
-int clif_getport(void);
+namespace map
+{
int clif_countusers(void);
void clif_setwaitclose(Session *);
@@ -57,7 +49,6 @@ int clif_charselectok(BlockId);
int clif_dropflooritem(dumb_ptr<flooritem_data>);
int clif_clearflooritem(dumb_ptr<flooritem_data>, Session *);
int clif_clearchar(dumb_ptr<block_list>, BeingRemoveWhy); // area or fd
-int clif_clearchar_delay(tick_t, dumb_ptr<block_list>, BeingRemoveWhy);
void clif_clearchar_id(BlockId, BeingRemoveWhy, Session *);
int clif_spawnpc(dumb_ptr<map_session_data>); //area
int clif_spawnnpc(dumb_ptr<npc_data>); // area
@@ -87,11 +78,11 @@ void clif_delitem(dumb_ptr<map_session_data>, IOff0, int); //self
int clif_updatestatus(dumb_ptr<map_session_data>, SP); //self
int clif_damage(dumb_ptr<block_list>, dumb_ptr<block_list>,
tick_t, interval_t, interval_t,
- int, int, DamageType, int); // area
+ int, int, DamageType); // area
inline
int clif_takeitem(dumb_ptr<block_list> src, dumb_ptr<block_list> dst)
{
- return clif_damage(src, dst, tick_t(), interval_t::zero(), interval_t::zero(), 0, 0, DamageType::TAKEITEM, 0);
+ return clif_damage(src, dst, tick_t(), interval_t::zero(), interval_t::zero(), 0, 0, DamageType::TAKEITEM);
}
int clif_changelook(dumb_ptr<block_list>, LOOK, int); // area
void clif_changelook_accessories(dumb_ptr<block_list> bl, dumb_ptr<map_session_data> dst); // area or target; list gloves, boots etc.
@@ -105,6 +96,7 @@ int clif_changeoption(dumb_ptr<block_list>); // area
int clif_useitemack(dumb_ptr<map_session_data>, IOff0, int, int); // self
void clif_emotion(dumb_ptr<block_list> bl, int type);
+void clif_emotion_towards(dumb_ptr<block_list> bl, dumb_ptr<block_list> target, int type);
void clif_sitting(Session *, dumb_ptr<map_session_data> sd);
// trade
@@ -119,12 +111,12 @@ int clif_tradecancelled(dumb_ptr<map_session_data> sd);
int clif_tradecompleted(dumb_ptr<map_session_data> sd, int fail);
// storage
-int clif_storageitemlist(dumb_ptr<map_session_data> sd, Storage *stor);
+int clif_storageitemlist(dumb_ptr<map_session_data> sd, Borrowed<Storage> stor);
int clif_storageequiplist(dumb_ptr<map_session_data> sd,
- Storage *stor);
+ Borrowed<Storage> stor);
int clif_updatestorageamount(dumb_ptr<map_session_data> sd,
- Storage *stor);
-int clif_storageitemadded(dumb_ptr<map_session_data> sd, Storage *stor,
+ Borrowed<Storage> stor);
+int clif_storageitemadded(dumb_ptr<map_session_data> sd, Borrowed<Storage> stor,
SOff0 index, int amount);
int clif_storageitemremoved(dumb_ptr<map_session_data> sd, SOff0 index,
int amount);
@@ -136,8 +128,6 @@ void clif_pcoutsight(dumb_ptr<block_list>, dumb_ptr<map_session_data>);
void clif_mobinsight(dumb_ptr<block_list>, dumb_ptr<mob_data>);
void clif_moboutsight(dumb_ptr<block_list>, dumb_ptr<mob_data>);
-int clif_skillinfo(dumb_ptr<map_session_data> sd, SkillID skillid, int type,
- int range);
void clif_skillinfoblock(dumb_ptr<map_session_data> sd);
int clif_skillup(dumb_ptr<map_session_data> sd, SkillID skill_num);
@@ -157,8 +147,6 @@ void clif_wis_end(Session *s, int flag);
void clif_itemlist(dumb_ptr<map_session_data> sd);
void clif_equiplist(dumb_ptr<map_session_data> sd);
-int clif_mvp_effect(dumb_ptr<map_session_data> sd);
-
int clif_movetoattack(dumb_ptr<map_session_data> sd, dumb_ptr<block_list> bl);
// party
@@ -187,6 +175,10 @@ int clif_GM_kick(dumb_ptr<map_session_data> sd, dumb_ptr<map_session_data> tsd,
int type);
int clif_foreachclient(std::function<void(dumb_ptr<map_session_data>)>);
+// quest
+void clif_sendallquest(dumb_ptr<map_session_data> sd);
+void clif_sendquest(dumb_ptr<map_session_data> sd, QuestId questid, int value);
void do_init_clif(void);
+} // namespace map
} // namespace tmwa
diff --git a/src/generic/intern-pool.cpp b/src/map/consts.hpp
index f45b098..a68d8e3 100644
--- a/src/generic/intern-pool.cpp
+++ b/src/map/consts.hpp
@@ -1,5 +1,5 @@
-#include "intern-pool.hpp"
-// intern-pool.cpp - Cached integer/string lookups.
+#pragma once
+// consts.hpp - Constants for tmwa-map.
//
// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
//
@@ -18,9 +18,17 @@
// 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 "../poison.hpp"
+#include "fwd.hpp"
+
+#include "../ints/udl.hpp"
+
+#include "../mmo/ids.hpp"
namespace tmwa
{
+namespace map
+{
+constexpr BlockId MAX_FLOORITEM = wrap<BlockId>(500000_u32);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/fwd.hpp b/src/map/fwd.hpp
index 79bbbcd..911d566 100644
--- a/src/map/fwd.hpp
+++ b/src/map/fwd.hpp
@@ -20,11 +20,38 @@
#include "../sanity.hpp"
+#include <cstdint>
+
+#include "../ints/fwd.hpp" // rank 1
+#include "../range/fwd.hpp" // rank 1
+#include "../strings/fwd.hpp" // rank 1
+#include "../compat/fwd.hpp" // rank 2
+#include "../generic/fwd.hpp" // rank 3
+#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 "../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
+
namespace tmwa
{
+namespace map
+{
// meh, add more when I feel like it
-class BlockId;
+struct BattleConf;
+struct MapConf;
+
+struct charid2nick;
+struct map_abstract;
+struct mob_db_;
+struct skill_db_;
+struct event_data;
+
struct block_list;
struct map_session_data;
struct npc_data;
@@ -36,9 +63,13 @@ class npc_data_script;
class npc_data_shop;
class npc_data_warp;
class npc_data_message;
-struct NpcEvent;
struct item_data;
+struct quest_data;
+
+struct ScriptState;
+struct str_data_t;
+class SIR;
namespace magic
{
@@ -55,5 +86,7 @@ struct env_t;
struct magic_conf_t;
struct component_t;
struct effect_set_t;
+struct proc_t;
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/globals.cpp b/src/map/globals.cpp
new file mode 100644
index 0000000..dce3906
--- /dev/null
+++ b/src/map/globals.cpp
@@ -0,0 +1,149 @@
+#include "globals.hpp"
+// globals.cpp - Evil global variables for tmwa-map.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "../generic/intern-pool.hpp"
+
+#include "../io/write.hpp"
+
+#include "../proto2/net-Storage.hpp"
+
+#include "battle_conf.hpp"
+#include "itemdb.hpp"
+#include "quest.hpp"
+#include "magic-interpreter.hpp"
+#include "map_conf.hpp"
+#include "mob.hpp"
+#include "npc-internal.hpp"
+#include "script-parse-internal.hpp"
+#include "skill.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+ namespace map
+ {
+ BattleConf battle_config;
+ MapConf map_conf;
+
+ // only used by intif.cpp
+ // and clif.cpp for the new on_delete stuff ...
+ Session *char_session;
+ int chrif_state;
+ std::map<MapName, RString> resnametable;
+ Map<ItemNameId, item_data> item_db;
+ Map<QuestId, quest_data> quest_db;
+ namespace magic
+ {
+ // Global magic conf
+ magic_conf_t magic_conf;
+ env_t magic_default_env = { &magic_conf, nullptr };
+ namespace magic_v2
+ {
+ std::map<RString, proc_t> procs;
+ std::map<RString, val_t> const_defm;
+ } // namespace magic_v2
+ } // namespace magic
+
+ DMap<BlockId, dumb_ptr<block_list>> id_db;
+ UPMap<MapName, map_abstract> maps_db;
+ DMap<CharName, dumb_ptr<map_session_data>> nick_db;
+ Map<CharId, charid2nick> charid_db;
+ int world_user_count = 0;
+ Array<dumb_ptr<block_list>, unwrap<BlockId>(MAX_FLOORITEM)> object;
+ BlockId first_free_object_id = BlockId();
+ int save_settings = 0xFFFF;
+ int block_free_lock = 0;
+ std::vector<dumb_ptr<block_list>> block_free;
+ /// This is a dummy entry that is shared by all the linked lists,
+ /// so that any entry can unlink itself without worrying about
+ /// whether it was the the head of the list.
+ block_list bl_head;
+ std::unique_ptr<io::AppendFile> map_logfile;
+ long map_logfile_index;
+ mob_db_ mob_db[2001];
+ std::list<AString> npc_srcs;
+ int npc_warp, npc_shop, npc_script, npc_mob;
+ BlockId npc_id = START_NPC_NUM;
+ Map<NpcEvent, struct event_data> ev_db;
+ DMap<NpcName, dumb_ptr<npc_data>> npcs_by_name;
+ // used for clock-based event triggers
+ // only tm_min, tm_hour, and tm_mday are used
+ tm ev_tm_b =
+ {
+ .tm_sec= 0,
+ .tm_min= -1,
+ .tm_hour= -1,
+ .tm_mday= -1,
+ .tm_mon= 0,
+ .tm_year= 0,
+ .tm_wday= 0,
+ .tm_yday= 0,
+ .tm_isdst= 0,
+ };
+ Map<PartyId, PartyMost> party_db;
+ std::map<AccountId, GmLevel> gm_accountm;
+ tick_t natural_heal_tick, natural_heal_prev_tick;
+ interval_t natural_heal_diff_tick;
+ int last_save_fd;
+ bool save_flag;
+ Map<AccountId, Storage> storage_db;
+
+ Map<RString, str_data_t> str_datam;
+ str_data_t LABEL_NEXTLINE_;
+ Map<ScriptLabel, int> scriptlabel_db;
+ std::set<ScriptLabel> probable_labels;
+ UPMap<RString, const ScriptBuffer> userfunc_db;
+ int parse_cmd_if = 0;
+ Option<Borrowed<str_data_t>> parse_cmdp = None;
+ InternPool variable_names;
+ // TODO: replace this whole mess with some sort of input stream that works
+ // a line at a time.
+ ZString startptr;
+ int startline;
+ int script_errors = 0;
+ DMap<SIR, int> mapreg_db;
+ Map<SIR, RString> mapregstr_db;
+ int mapreg_dirty = -1;
+
+ std::vector<SkillID> skill_pool_skills;
+ earray<skill_db_, SkillID, SkillID::MAX_SKILL_DB> skill_db;
+ // these variables are set in the 'else' branches,
+ // and used in the (recursive) 'if' branch
+ // TODO kill it, kill it with fire.
+ BlockId skill_area_temp_id;
+ int skill_area_temp_hp;
+
+ // Some other globals are not moved here, because they are
+ // large and initialized in-place and then *mostly* unmodified.
+ //
+ // src/map/atcommand.cpp:
+ // Map<XString, AtCommandInfo> atcommand_info;
+ // src/map/script-fun.cpp:
+ // BuiltinFunction builtin_functions[];
+ // src/map/clif.cpp:
+ // func_table clif_parse_func_table[0x0220];
+ // src/map/magic-expr.cpp:
+ // std::map<ZString, fun_t> functions;
+ // src/map/magic-stmt.cpp:
+ // std::map<ZString, op_t> operations;
+ } // namespace map
+} // namespace tmwa
diff --git a/src/map/globals.hpp b/src/map/globals.hpp
new file mode 100644
index 0000000..b457b4e
--- /dev/null
+++ b/src/map/globals.hpp
@@ -0,0 +1,109 @@
+#pragma once
+// globals.hpp - Evil global variables for tmwa-map.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+#include <ctime>
+
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include "../ints/wrap.hpp"
+
+#include "../net/timer.t.hpp"
+
+#include "../mmo/skill.t.hpp"
+
+#include "consts.hpp"
+#include "script-buffer.hpp"
+
+
+namespace tmwa
+{
+ namespace map
+ {
+ extern BattleConf battle_config;
+ extern MapConf map_conf;
+ extern Session *char_session;
+ extern int chrif_state;
+ extern std::map<MapName, RString> resnametable;
+ extern Map<ItemNameId, item_data> item_db;
+ extern Map<QuestId, quest_data> quest_db;
+ namespace magic
+ {
+ // Global magic conf
+ extern magic_conf_t magic_conf;
+ extern env_t magic_default_env;
+ namespace magic_v2
+ {
+ extern std::map<RString, proc_t> procs;
+ extern std::map<RString, val_t> const_defm;
+ } // namespace magic_v2
+ } // namespace magic
+ extern DMap<BlockId, dumb_ptr<block_list>> id_db;
+ extern UPMap<MapName, map_abstract> maps_db;
+ extern DMap<CharName, dumb_ptr<map_session_data>> nick_db;
+ extern Map<CharId, charid2nick> charid_db;
+ extern int world_user_count;
+ extern Array<dumb_ptr<block_list>, unwrap<BlockId>(MAX_FLOORITEM)> object;
+ extern BlockId first_free_object_id;
+ extern int save_settings;
+ extern int block_free_lock;
+ extern std::vector<dumb_ptr<block_list>> block_free;
+ extern block_list bl_head;
+ extern std::unique_ptr<io::AppendFile> map_logfile;
+ extern long map_logfile_index;
+ extern mob_db_ mob_db[2001];
+ extern std::list<AString> npc_srcs;
+ extern int npc_warp, npc_shop, npc_script, npc_mob;
+ extern BlockId npc_id;
+ extern Map<NpcEvent, event_data> ev_db;
+ extern DMap<NpcName, dumb_ptr<npc_data>> npcs_by_name;
+ extern tm ev_tm_b;
+ extern Map<PartyId, PartyMost> party_db;
+ extern std::map<AccountId, GmLevel> gm_accountm;
+ extern tick_t natural_heal_tick, natural_heal_prev_tick;
+ extern interval_t natural_heal_diff_tick;
+ extern int last_save_fd;
+ extern bool save_flag;
+ extern Map<AccountId, Storage> storage_db;
+ extern Map<RString, str_data_t> str_datam;
+ extern str_data_t LABEL_NEXTLINE_;
+ extern Map<ScriptLabel, int> scriptlabel_db;
+ extern std::set<ScriptLabel> probable_labels;
+ extern UPMap<RString, const ScriptBuffer> userfunc_db;
+ extern int parse_cmd_if;
+ extern Option<Borrowed<str_data_t>> parse_cmdp;
+ extern InternPool variable_names;
+ extern ZString startptr;
+ extern int startline;
+ extern int script_errors;
+ extern DMap<SIR, int> mapreg_db;
+ extern Map<SIR, RString> mapregstr_db;
+ extern int mapreg_dirty;
+ extern std::vector<SkillID> skill_pool_skills;
+ extern earray<skill_db_, SkillID, SkillID::MAX_SKILL_DB> skill_db;
+ extern BlockId skill_area_temp_id;
+ extern int skill_area_temp_hp;
+ } // namespace map
+} // namespace tmwa
diff --git a/src/map/grfio.cpp b/src/map/grfio.cpp
index 4a1656b..3475108 100644
--- a/src/map/grfio.cpp
+++ b/src/map/grfio.cpp
@@ -33,19 +33,21 @@
#include "../strings/zstring.hpp"
#include "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
#include "../io/read.hpp"
-#include "../mmo/extract.hpp"
-#include "../mmo/mmo.hpp"
+#include "../high/extract_mmo.hpp"
+#include "../high/mmo.hpp"
+
+#include "globals.hpp"
#include "../poison.hpp"
namespace tmwa
{
-static
-std::map<MapName, RString> resnametable;
-
+namespace map
+{
bool load_resnametable(ZString filename)
{
io::ReadFile in(filename);
@@ -106,4 +108,5 @@ std::vector<uint8_t> grfio_reads(MapName rname)
close(fd);
return buffer;
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/grfio.hpp b/src/map/grfio.hpp
index d2ab058..25e27ef 100644
--- a/src/map/grfio.hpp
+++ b/src/map/grfio.hpp
@@ -26,17 +26,16 @@
#include <vector>
-#include "../strings/fwd.hpp"
-
-#include "../mmo/fwd.hpp"
-
namespace tmwa
{
+namespace map
+{
bool load_resnametable(ZString filename);
/// Load a resource into memory, subject to data/resnametable.txt.
/// Normally, resourcename is xxx-y.gat and the file is xxx-y.wlk.
/// Currently there is exactly one .wlk per .gat, but multiples are fine.
std::vector<uint8_t> grfio_reads(MapName resourcename);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/intif.cpp b/src/map/intif.cpp
index 314db24..a5709ef 100644
--- a/src/map/intif.cpp
+++ b/src/map/intif.cpp
@@ -29,16 +29,19 @@
#include "../io/cxxstdio.hpp"
-#include "../net/packets.hpp"
#include "../net/socket.hpp"
-#include "../mmo/mmo.hpp"
+#include "../high/mmo.hpp"
#include "../proto2/char-map.hpp"
+#include "../wire/packets.hpp"
+
#include "battle.hpp"
+#include "battle_conf.hpp"
#include "chrif.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "map.hpp"
#include "party.hpp"
#include "pc.hpp"
@@ -49,6 +52,8 @@
namespace tmwa
{
+namespace map
+{
//-----------------------------------------------------------------
// inter serverへの送信
@@ -156,9 +161,8 @@ void intif_request_storage(AccountId account_id)
}
// 倉庫データ送信
-void intif_send_storage(Storage *stor)
+void intif_send_storage(Borrowed<Storage> stor)
{
- nullpo_retv(stor);
if (!char_session)
return;
@@ -386,7 +390,6 @@ int intif_parse_AccountReg(Session *, const Packet_Head<0x3804>& head, const std
static
int intif_parse_LoadStorage(Session *, const Packet_Payload<0x3810>& payload)
{
- Storage *stor;
dumb_ptr<map_session_data> sd;
sd = map_id2sd(account_to_block(payload.account_id));
@@ -397,7 +400,7 @@ int intif_parse_LoadStorage(Session *, const Packet_Payload<0x3810>& payload)
payload.account_id);
return 1;
}
- stor = account2storage(payload.account_id);
+ P<Storage> stor = account2storage(payload.account_id);
if (stor->storage_status == 1)
{ // Already open.. lets ignore this update
if (battle_config.error_log)
@@ -463,9 +466,7 @@ void intif_parse_PartyInfo(Session *, const Packet_Head<0x3821>& head, bool has_
PartyId pi = head.party_id;
PartyMost pm = option.party_most;
- PartyPair pp;
- pp.party_id = pi;
- pp.party_most = &pm;
+ PartyPair pp{pi, borrow(pm)};
party_recv_info(pp);
}
@@ -697,4 +698,5 @@ RecvResult intif_parse(Session *s, uint16_t packet_id)
}
return rv;
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/intif.hpp b/src/map/intif.hpp
index 5be61a9..ac68040 100644
--- a/src/map/intif.hpp
+++ b/src/map/intif.hpp
@@ -22,17 +22,11 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
-#include "../generic/fwd.hpp"
-
-#include "../net/fwd.hpp"
-
-#include "../mmo/fwd.hpp"
-
namespace tmwa
{
+namespace map
+{
RecvResult intif_parse(Session *, uint16_t packet_id);
void intif_GMmessage(XString mes);
@@ -44,7 +38,7 @@ void intif_saveaccountreg(dumb_ptr<map_session_data> sd);
void intif_request_accountreg(dumb_ptr<map_session_data> sd);
void intif_request_storage(AccountId account_id);
-void intif_send_storage(Storage *stor);
+void intif_send_storage(Borrowed<Storage> stor);
void intif_create_party(dumb_ptr<map_session_data> sd, PartyName name);
void intif_request_partyinfo(PartyId party_id);
@@ -55,4 +49,5 @@ void intif_party_leave(PartyId party_id, AccountId accound_id);
void intif_party_changemap(dumb_ptr<map_session_data> sd, int online);
void intif_party_message(PartyId party_id, AccountId account_id, XString mes);
void intif_party_checkconflict(PartyId party_id, AccountId account_id, CharName nick);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/itemdb.cpp b/src/map/itemdb.cpp
index 50cc5c4..7dd725e 100644
--- a/src/map/itemdb.cpp
+++ b/src/map/itemdb.cpp
@@ -29,20 +29,24 @@
#include "../generic/db.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/read.hpp"
+#include "../io/extract.hpp"
+#include "../io/line.hpp"
#include "../mmo/config_parse.hpp"
-#include "../mmo/extract.hpp"
#include "../mmo/extract_enums.hpp"
+#include "../ast/item.hpp"
+
+#include "globals.hpp"
+#include "script-parse.hpp"
+
#include "../poison.hpp"
namespace tmwa
{
-static
-Map<ItemNameId, struct item_data> item_db;
-
+namespace map
+{
// Function declarations
/*==========================================
@@ -51,24 +55,24 @@ Map<ItemNameId, struct item_data> item_db;
*/
// name = item alias, so we should find items aliases first. if not found then look for "jname" (full name)
static
-void itemdb_searchname_sub(struct item_data *item, ItemName str, struct item_data **dst)
+void itemdb_searchname_sub(Borrowed<struct item_data> item, ItemName str, Borrowed<Option<Borrowed<struct item_data>>> dst)
{
if (item->name == str)
- *dst = item;
+ *dst = Some(item);
}
/*==========================================
* 名前で検索
*------------------------------------------
*/
-struct item_data *itemdb_searchname(XString str_)
+Option<Borrowed<struct item_data>> itemdb_searchname(XString str_)
{
ItemName str = stringish<ItemName>(str_);
if (XString(str) != str_)
- return nullptr;
- struct item_data *item = nullptr;
+ return None;
+ Option<P<struct item_data>> item = None;
for (auto& pair : item_db)
- itemdb_searchname_sub(&pair.second, str, &item);
+ itemdb_searchname_sub(borrow(pair.second), str, borrow(item));
return item;
}
@@ -76,7 +80,7 @@ struct item_data *itemdb_searchname(XString str_)
* DBの存在確認
*------------------------------------------
*/
-struct item_data *itemdb_exists(ItemNameId nameid)
+Option<Borrowed<struct item_data>> itemdb_exists(ItemNameId nameid)
{
return item_db.search(nameid);
}
@@ -85,13 +89,16 @@ struct item_data *itemdb_exists(ItemNameId nameid)
* DBの検索
*------------------------------------------
*/
-struct item_data *itemdb_search(ItemNameId nameid)
+Borrowed<struct item_data> itemdb_search(ItemNameId nameid)
{
- struct item_data *id = item_db.search(nameid);
- if (id)
+ Option<P<struct item_data>> id_ = item_db.search(nameid);
+ OMATCH_BEGIN_SOME (id, id_)
+ {
return id;
+ }
+ OMATCH_END ();
- id = item_db.init(nameid);
+ P<struct item_data> id = item_db.init(nameid);
id->nameid = nameid;
id->value_buy = 10;
@@ -123,10 +130,8 @@ int itemdb_isequip(ItemNameId nameid)
*
*------------------------------------------
*/
-int itemdb_isequip2(struct item_data *data)
+bool itemdb_isequip2(Borrowed<struct item_data> data)
{
- if (!data)
- return false;
ItemType type = data->type;
return !(type == ItemType::USE
|| type == ItemType::_2
@@ -149,99 +154,64 @@ 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_BEGIN (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)
- )
- )
- )
- {
- PRINTF("%s:%d: error: bad item line: %s\n"_fmt,
- filename, lines, line);
- rv = false;
- continue;
- }
-
- ln++;
-
- struct item_data *id = itemdb_search(idv.nameid);
- *id = std::move(idv);
- if (id->value_buy == 0 && id->value_sell == 0)
+ MATCH_CASE (const ast::item::Comment&, c)
{
+ (void)c;
}
- else if (id->value_buy == 0)
+ MATCH_CASE (const ast::item::Item&, item)
{
- id->value_buy = id->value_sell * 2;
+ 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(STRPRINTF("use script %d"_fmt, idv.nameid), item.use_script, true);
+ idv.equip_script = compile_script(STRPRINTF("equip script %d"_fmt, idv.nameid), item.equip_script, true);
+
+ Borrowed<struct item_data> id = itemdb_search(idv.nameid);
+ *id = std::move(idv);
}
- 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);
+ MATCH_END ();
}
-
- return rv;
}
/*==========================================
@@ -265,4 +235,5 @@ void do_final_itemdb(void)
itemdb_final(&pair.second);
item_db.clear();
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/itemdb.hpp b/src/map/itemdb.hpp
index 25b4dad..5e19c0b 100644
--- a/src/map/itemdb.hpp
+++ b/src/map/itemdb.hpp
@@ -23,14 +23,16 @@
#include "fwd.hpp"
#include "../mmo/ids.hpp"
-#include "../mmo/mmo.hpp"
+#include "../high/mmo.hpp"
#include "map.t.hpp"
-#include "script.hpp"
+#include "script-buffer.hpp"
namespace tmwa
{
+namespace map
+{
struct item_data
{
ItemNameId nameid;
@@ -59,11 +61,11 @@ struct random_item_data
};
inline
-struct item_data *itemdb_searchname(ItemName) = delete;
-struct item_data *itemdb_searchname(XString name);
+Option<Borrowed<struct item_data>> itemdb_searchname(ItemName) = delete;
+Option<Borrowed<struct item_data>> itemdb_searchname(XString name);
// TODO this function should die
-struct item_data *itemdb_search(ItemNameId nameid);
-struct item_data *itemdb_exists(ItemNameId nameid);
+Borrowed<struct item_data> itemdb_search(ItemNameId nameid);
+Option<Borrowed<struct item_data>> itemdb_exists(ItemNameId nameid);
inline
ItemType itemdb_type(ItemNameId n)
@@ -97,11 +99,12 @@ int itemdb_value_sell(ItemNameId n)
}
int itemdb_isequip(ItemNameId);
-int itemdb_isequip2(struct item_data *);
+bool itemdb_isequip2(Borrowed<struct item_data>);
int itemdb_isequip3(ItemNameId);
void itemdb_reload(void);
void do_final_itemdb(void);
bool itemdb_readdb(ZString filename);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic-expr-eval.hpp b/src/map/magic-expr-eval.hpp
index 4529c04..e8ed4aa 100644
--- a/src/map/magic-expr-eval.hpp
+++ b/src/map/magic-expr-eval.hpp
@@ -28,6 +28,8 @@
namespace tmwa
{
+namespace map
+{
namespace magic
{
// TODO soon kill this unlike I killed VAR
@@ -48,4 +50,5 @@ namespace magic
#define ARG_MAY_BE_AREA(x) (args[x].is<ValArea>() || args[x].is<ValArea>())
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic-expr.cpp b/src/map/magic-expr.cpp
index 674c850..197727e 100644
--- a/src/map/magic-expr.cpp
+++ b/src/map/magic-expr.cpp
@@ -33,7 +33,8 @@
#include "../generic/random.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
+
+#include "../mmo/cxxstdio_enums.hpp"
#include "battle.hpp"
#include "itemdb.hpp"
@@ -42,12 +43,15 @@
#include "magic-interpreter-base.hpp"
#include "npc.hpp"
#include "pc.hpp"
+#include "script-call.hpp"
#include "../poison.hpp"
namespace tmwa
{
+namespace map
+{
namespace magic
{
static
@@ -56,14 +60,15 @@ void free_area(dumb_ptr<area_t> area)
if (!area)
return;
- MATCH (*area)
+ MATCH_BEGIN (*area)
{
- CASE (const AreaUnion&, a)
+ MATCH_CASE (const AreaUnion&, a)
{
free_area(a.a_union[0]);
free_area(a.a_union[1]);
}
}
+ MATCH_END ();
area.delete_();
}
@@ -71,109 +76,111 @@ void free_area(dumb_ptr<area_t> area)
static
dumb_ptr<area_t> dup_area(dumb_ptr<area_t> area)
{
- MATCH (*area)
+ MATCH_BEGIN (*area)
{
- CASE (const location_t&, loc)
+ MATCH_CASE (const location_t&, loc)
{
return dumb_ptr<area_t>::make(loc);
}
- CASE (const AreaUnion&, a)
+ MATCH_CASE (const AreaUnion&, a)
{
AreaUnion u;
u.a_union[0] = dup_area(a.a_union[0]);
u.a_union[1] = dup_area(a.a_union[1]);
return dumb_ptr<area_t>::make(u);
}
- CASE (const AreaRect&, rect)
+ MATCH_CASE (const AreaRect&, rect)
{
return dumb_ptr<area_t>::make(rect);
}
- CASE (const AreaBar&, bar)
+ MATCH_CASE (const AreaBar&, bar)
{
return dumb_ptr<area_t>::make(bar);
}
}
+ MATCH_END ();
abort();
}
void magic_copy_var(val_t *dest, const val_t *src)
{
- MATCH (*src)
+ MATCH_BEGIN (*src)
{
- // mumble mumble not a public API ...
- default:
+ MATCH_DEFAULT ()
{
abort();
}
- CASE (const ValUndef&, s)
+ MATCH_CASE (const ValUndef&, s)
{
*dest = s;
}
- CASE (const ValInt&, s)
+ MATCH_CASE (const ValInt&, s)
{
*dest = s;
}
- CASE (const ValDir&, s)
+ MATCH_CASE (const ValDir&, s)
{
*dest = s;
}
- CASE (const ValString&, s)
+ MATCH_CASE (const ValString&, s)
{
*dest = ValString{s.v_string};
}
- CASE (const ValEntityInt&, s)
+ MATCH_CASE (const ValEntityInt&, s)
{
*dest = s;
}
- CASE (const ValEntityPtr&, s)
+ MATCH_CASE (const ValEntityPtr&, s)
{
*dest = s;
}
- CASE (const ValLocation&, s)
+ MATCH_CASE (const ValLocation&, s)
{
*dest = s;
}
- CASE (const ValArea&, s)
+ MATCH_CASE (const ValArea&, s)
{
*dest = ValArea{dup_area(s.v_area)};
}
- CASE (const ValSpell&, s)
+ MATCH_CASE (const ValSpell&, s)
{
*dest = s;
}
- CASE (const ValInvocationInt&, s)
+ MATCH_CASE (const ValInvocationInt&, s)
{
*dest = s;
}
- CASE (const ValInvocationPtr&, s)
+ MATCH_CASE (const ValInvocationPtr&, s)
{
*dest = s;
}
- CASE (const ValFail&, s)
+ MATCH_CASE (const ValFail&, s)
{
*dest = s;
}
- CASE (const ValNegative1&, s)
+ MATCH_CASE (const ValNegative1&, s)
{
*dest = s;
}
}
+ MATCH_END ();
}
void magic_clear_var(val_t *v)
{
- MATCH (*v)
+ MATCH_BEGIN (*v)
{
- CASE (ValString&, s)
+ MATCH_CASE (ValString&, s)
{
(void)s;
}
- CASE (const ValArea&, a)
+ MATCH_CASE (const ValArea&, a)
{
free_area(a.v_area);
}
}
+ MATCH_END ();
}
static
@@ -212,63 +219,64 @@ void stringify(val_t *v)
}};
AString buf;
- MATCH (*v)
+ MATCH_BEGIN (*v)
{
- default:
+ MATCH_DEFAULT ()
{
abort();
}
- CASE (const ValUndef&, x)
+ MATCH_CASE (const ValUndef&, x)
{
(void)x;
buf = "UNDEF"_s;
}
- CASE (const ValInt&, x)
+ MATCH_CASE (const ValInt&, x)
{
buf = STRPRINTF("%i"_fmt, x.v_int);
}
- CASE (const ValString&, x)
+ MATCH_CASE (const ValString&, x)
{
(void)x;
return;
}
- CASE (const ValDir&, x)
+ MATCH_CASE (const ValDir&, x)
{
buf = dirs[x.v_dir];
}
- CASE (const ValEntityPtr&, x)
+ MATCH_CASE (const ValEntityPtr&, x)
{
buf = show_entity(x.v_entity);
}
- CASE (const ValLocation&, x)
+ MATCH_CASE (const ValLocation&, x)
{
buf = STRPRINTF("<\"%s\", %d, %d>"_fmt,
x.v_location.m->name_,
x.v_location.x,
x.v_location.y);
}
- CASE (const ValArea&, x)
+ MATCH_CASE (const ValArea&, x)
{
buf = "%area"_s;
free_area(x.v_area);
}
- CASE (const ValSpell&, x)
+ MATCH_CASE (const ValSpell&, x)
{
buf = x.v_spell->name;
}
- CASE (const ValInvocationInt&, x)
+ MATCH_CASE (const ValInvocationInt&, x)
{
dumb_ptr<invocation> invocation_ =
map_id2bl(x.v_iid)->is_spell();
buf = invocation_->spell->name;
}
- CASE (const ValInvocationPtr&, x)
+ MATCH_CASE (const ValInvocationPtr&, x)
{
dumb_ptr<invocation> invocation_ =
x.v_invocation;
buf = invocation_->spell->name;
}
}
+ MATCH_END ();
*v = ValString{buf};
}
@@ -310,14 +318,15 @@ void make_location(val_t *v)
{
if (ValArea *a = v->get_if<ValArea>())
{
- MATCH (*a->v_area)
+ MATCH_BEGIN (*a->v_area)
{
- CASE (const location_t&, location)
+ MATCH_CASE (const location_t&, location)
{
free_area(a->v_area);
*v = ValLocation{location};
}
}
+ MATCH_END ();
}
}
@@ -582,39 +591,41 @@ int fun_if_then_else(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
return 0;
}
-void magic_area_rect(map_local **m, int *x, int *y, int *width, int *height,
+Borrowed<map_local> magic_area_rect(int *x, int *y, int *width, int *height,
area_t& area_)
{
- MATCH (area_)
+ MATCH_BEGIN (area_)
{
- CASE (const AreaUnion&, a)
+ MATCH_CASE (const AreaUnion&, a)
{
(void)a;
abort();
}
- CASE (const location_t&, a_loc)
+ MATCH_CASE (const location_t&, a_loc)
{
- *m = a_loc.m;
+ P<map_local> m = a_loc.m;
*x = a_loc.x;
*y = a_loc.y;
*width = 1;
*height = 1;
+ return m;
}
- CASE (const AreaRect&, a_rect)
+ MATCH_CASE (const AreaRect&, a_rect)
{
- *m = a_rect.loc.m;
+ P<map_local> m = a_rect.loc.m;
*x = a_rect.loc.x;
*y = a_rect.loc.y;
*width = a_rect.width;
*height = a_rect.height;
+ return m;
}
- CASE (const AreaBar&, a_bar)
+ MATCH_CASE (const AreaBar&, a_bar)
{
int tx = a_bar.loc.x;
int ty = a_bar.loc.y;
int twidth = a_bar.width;
int tdepth = a_bar.width;
- *m = a_bar.loc.m;
+ P<map_local> m = a_bar.loc.m;
switch (a_bar.dir)
{
@@ -653,53 +664,54 @@ void magic_area_rect(map_local **m, int *x, int *y, int *width, int *height,
*y = ty;
*width = *height = 1;
}
+ return m;
}
}
+ MATCH_END ();
+ abort();
}
-int magic_location_in_area(map_local *m, int x, int y, dumb_ptr<area_t> area)
+int magic_location_in_area(Borrowed<map_local> m, int x, int y, dumb_ptr<area_t> area)
{
- MATCH (*area)
+ MATCH_BEGIN (*area)
{
- CASE (const AreaUnion&, a)
+ MATCH_CASE (const AreaUnion&, a)
{
return magic_location_in_area(m, x, y, a.a_union[0])
|| magic_location_in_area(m, x, y, a.a_union[1]);
}
- CASE (const location_t&, a_loc)
+ MATCH_CASE (const location_t&, a_loc)
{
(void)a_loc;
// TODO this can be simplified
- map_local *am;
int ax, ay, awidth, aheight;
- magic_area_rect(&am, &ax, &ay, &awidth, &aheight, *area);
+ P<map_local> am = magic_area_rect(&ax, &ay, &awidth, &aheight, *area);
return (am == m
&& (x >= ax) && (y >= ay)
&& (x < ax + awidth) && (y < ay + aheight));
}
- CASE (const AreaRect&, a_rect)
+ MATCH_CASE (const AreaRect&, a_rect)
{
(void)a_rect;
// TODO this is too complicated
- map_local *am;
int ax, ay, awidth, aheight;
- magic_area_rect(&am, &ax, &ay, &awidth, &aheight, *area);
+ P<map_local> am = magic_area_rect(&ax, &ay, &awidth, &aheight, *area);
return (am == m
&& (x >= ax) && (y >= ay)
&& (x < ax + awidth) && (y < ay + aheight));
}
- CASE (const AreaBar&, a_bar)
+ MATCH_CASE (const AreaBar&, a_bar)
{
(void)a_bar;
// TODO this is wrong
- map_local *am;
int ax, ay, awidth, aheight;
- magic_area_rect(&am, &ax, &ay, &awidth, &aheight, *area);
+ P<map_local> am = magic_area_rect(&ax, &ay, &awidth, &aheight, *area);
return (am == m
&& (x >= ax) && (y >= ay)
&& (x < ax + awidth) && (y < ay + aheight));
}
}
+ MATCH_END ();
abort();
}
@@ -872,18 +884,17 @@ int fun_hash_entity(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
// ret -1: not a string, ret 1: no such item, ret 0: OK
int magic_find_item(Slice<val_t> args, int index, Item *item_, int *stackable)
{
- struct item_data *item_data;
+ Option<P<struct item_data>> item_data_ = None;
int must_add_sequentially;
if (args[index].is<ValInt>())
- item_data = itemdb_exists(wrap<ItemNameId>(static_cast<uint16_t>(ARGINT(index))));
+ item_data_ = itemdb_exists(wrap<ItemNameId>(static_cast<uint16_t>(ARGINT(index))));
else if (args[index].is<ValString>())
- item_data = itemdb_searchname(ARGSTR(index));
+ item_data_ = itemdb_searchname(ARGSTR(index));
else
return -1;
- if (!item_data)
- return 1;
+ P<struct item_data> item_data = TRY_UNWRAP(item_data_, return 1);
// Very elegant.
must_add_sequentially = (
@@ -1082,22 +1093,21 @@ int fun_line_of_sight(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
void magic_random_location(location_t *dest, dumb_ptr<area_t> area)
{
- MATCH (*area)
+ MATCH_BEGIN (*area)
{
- CASE (const AreaUnion&, a)
+ MATCH_CASE (const AreaUnion&, a)
{
if (random_::chance({a.a_union[0]->size, area->size}))
magic_random_location(dest, a.a_union[0]);
else
magic_random_location(dest, a.a_union[1]);
}
- CASE (const location_t&, a_loc)
+ MATCH_CASE (const location_t&, a_loc)
{
(void)a_loc;
// TODO this can be simplified
- map_local *m;
int x, y, w, h;
- magic_area_rect(&m, &x, &y, &w, &h, *area);
+ P<map_local> m = magic_area_rect(&x, &y, &w, &h, *area);
if (w <= 1)
w = 1;
@@ -1113,13 +1123,12 @@ void magic_random_location(location_t *dest, dumb_ptr<area_t> area)
dest->x = pair.first;
dest->y = pair.second;
}
- CASE (const AreaRect&, a_rect)
+ MATCH_CASE (const AreaRect&, a_rect)
{
(void)a_rect;
// TODO this can be simplified
- map_local *m;
int x, y, w, h;
- magic_area_rect(&m, &x, &y, &w, &h, *area);
+ P<map_local> m = magic_area_rect(&x, &y, &w, &h, *area);
if (w <= 1)
w = 1;
@@ -1135,13 +1144,12 @@ void magic_random_location(location_t *dest, dumb_ptr<area_t> area)
dest->x = pair.first;
dest->y = pair.second;
}
- CASE (const AreaBar&, a_bar)
+ MATCH_CASE (const AreaBar&, a_bar)
{
(void)a_bar;
// TODO this is wrong
- map_local *m;
int x, y, w, h;
- magic_area_rect(&m, &x, &y, &w, &h, *area);
+ P<map_local> m = magic_area_rect(&x, &y, &w, &h, *area);
if (w <= 1)
w = 1;
@@ -1158,6 +1166,7 @@ void magic_random_location(location_t *dest, dumb_ptr<area_t> area)
dest->y = pair.second;
}
}
+ MATCH_END ();
}
static
@@ -1228,7 +1237,7 @@ int fun_running_status_update(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
static
int fun_status_option(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
{
- *result = ValInt{(bool((ARGPC(0))->status.option & static_cast<Option>(ARGINT(1))))};
+ *result = ValInt{(bool((ARGPC(0))->status.option & static_cast<Opt0>(ARGINT(1))))};
return 0;
}
@@ -1511,10 +1520,8 @@ int eval_location(dumb_ptr<env_t> env, location_t *dest, const e_location_t *exp
&& x.is<ValInt>() && y.is<ValInt>())
{
MapName name = VString<15>(ZString(m.get_if<ValString>()->v_string));
- map_local *map_id = map_mapname2mapid(name);
magic_clear_var(&m);
- if (!map_id)
- return 1;
+ P<map_local> map_id = TRY_UNWRAP(map_mapname2mapid(name), return 1);
dest->m = map_id;
dest->x = x.get_if<ValInt>()->v_int;
dest->y = y.get_if<ValInt>()->v_int;
@@ -1532,9 +1539,9 @@ int eval_location(dumb_ptr<env_t> env, location_t *dest, const e_location_t *exp
static
dumb_ptr<area_t> eval_area(dumb_ptr<env_t> env, const e_area_t& expr_)
{
- MATCH (expr_)
+ MATCH_BEGIN (expr_)
{
- CASE (const e_location_t&, a_loc)
+ MATCH_CASE (const e_location_t&, a_loc)
{
location_t loc;
if (eval_location(env, &loc, &a_loc))
@@ -1546,7 +1553,7 @@ dumb_ptr<area_t> eval_area(dumb_ptr<env_t> env, const e_area_t& expr_)
return dumb_ptr<area_t>::make(loc);
}
}
- CASE (const ExprAreaUnion&, a)
+ MATCH_CASE (const ExprAreaUnion&, a)
{
AreaUnion u;
bool fail = false;
@@ -1568,7 +1575,7 @@ dumb_ptr<area_t> eval_area(dumb_ptr<env_t> env, const e_area_t& expr_)
}
return dumb_ptr<area_t>::make(u);
}
- CASE (const ExprAreaRect&, a_rect)
+ MATCH_CASE (const ExprAreaRect&, a_rect)
{
val_t width, height;
magic_eval(env, &width, a_rect.width);
@@ -1594,7 +1601,7 @@ dumb_ptr<area_t> eval_area(dumb_ptr<env_t> env, const e_area_t& expr_)
return nullptr;
}
}
- CASE (const ExprAreaBar&, a_bar)
+ MATCH_CASE (const ExprAreaBar&, a_bar)
{
val_t width, depth, dir;
magic_eval(env, &width, a_bar.width);
@@ -1626,6 +1633,7 @@ dumb_ptr<area_t> eval_area(dumb_ptr<env_t> env, const e_area_t& expr_)
}
}
}
+ MATCH_END ();
abort();
}
@@ -1744,14 +1752,14 @@ int magic_signature_check(ZString opname, ZString funname, ZString signature,
void magic_eval(dumb_ptr<env_t> env, val_t *dest, dumb_ptr<expr_t> expr)
{
- MATCH (*expr)
+ MATCH_BEGIN (*expr)
{
- CASE (const val_t&, e_val)
+ MATCH_CASE (const val_t&, e_val)
{
magic_copy_var(dest, &e_val);
}
- CASE (const e_location_t&, e_location)
+ MATCH_CASE (const e_location_t&, e_location)
{
location_t loc;
if (eval_location(env, &loc, &e_location))
@@ -1759,14 +1767,14 @@ void magic_eval(dumb_ptr<env_t> env, val_t *dest, dumb_ptr<expr_t> expr)
else
*dest = ValLocation{loc};
}
- CASE (const e_area_t&, e_area)
+ MATCH_CASE (const e_area_t&, e_area)
{
if (dumb_ptr<area_t> area = eval_area(env, e_area))
*dest = ValArea{area};
else
*dest = ValFail();
}
- CASE (const ExprFunApp&, e_funapp)
+ MATCH_CASE (const ExprFunApp&, e_funapp)
{
val_t arguments[MAX_ARGS];
int args_nr = e_funapp.args_nr;
@@ -1800,12 +1808,12 @@ void magic_eval(dumb_ptr<env_t> env, val_t *dest, dumb_ptr<expr_t> expr)
for (i = 0; i < args_nr; ++i)
magic_clear_var(&arguments[i]);
}
- CASE (const ExprId&, e)
+ MATCH_CASE (const ExprId&, e)
{
val_t& v = env->VAR(e.e_id);
magic_copy_var(dest, &v);
}
- CASE (const ExprField&, e_field)
+ MATCH_CASE (const ExprField&, e_field)
{
val_t v;
int id = e_field.id;
@@ -1833,6 +1841,7 @@ void magic_eval(dumb_ptr<env_t> env, val_t *dest, dumb_ptr<expr_t> expr)
}
}
}
+ MATCH_END ();
}
int magic_eval_int(dumb_ptr<env_t> env, dumb_ptr<expr_t> expr)
@@ -1861,4 +1870,5 @@ AString magic_eval_str(dumb_ptr<env_t> env, dumb_ptr<expr_t> expr)
return result.get_if<ValString>()->v_string;
}
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic-expr.hpp b/src/map/magic-expr.hpp
index 294e665..055f37b 100644
--- a/src/map/magic-expr.hpp
+++ b/src/map/magic-expr.hpp
@@ -21,20 +21,16 @@
#include "fwd.hpp"
-#include "../generic/fwd.hpp"
-
-#include "../range/fwd.hpp"
-
#include "../strings/zstring.hpp"
#include "../strings/literal.hpp"
-#include "../mmo/fwd.hpp"
-
#include "magic-interpreter.t.hpp"
namespace tmwa
{
+namespace map
+{
namespace magic
{
/*
@@ -97,14 +93,15 @@ int magic_find_item(Slice<val_t> args, int index, Item *item, int *stackable);
default: break; \
}
-int magic_location_in_area(map_local *m, int x, int y, dumb_ptr<area_t> area);
+int magic_location_in_area(Borrowed<map_local> m, int x, int y, dumb_ptr<area_t> area);
/* Helper definitions for dealing with functions and operations */
int magic_signature_check(ZString opname, ZString funname, ZString signature,
Slice<val_t> args, int line, int column);
-void magic_area_rect(map_local **m, int *x, int *y, int *width, int *height,
+Borrowed<map_local> magic_area_rect(int *x, int *y, int *width, int *height,
area_t& area);
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic-expr.py b/src/map/magic-expr.py
index 0d9db55..f53ddc8 100644
--- a/src/map/magic-expr.py
+++ b/src/map/magic-expr.py
@@ -1,7 +1,7 @@
class fun_t(object):
__slots__ = ('_value')
- name = 'tmwa::magic::fun_t'
+ name = 'tmwa::map::magic::fun_t'
depth = 1
enabled = True
@@ -31,8 +31,8 @@ class fun_t(object):
'''
tests = [
- ('static_cast<tmwa::magic::fun_t *>(nullptr)',
+ ('static_cast<tmwa::map::magic::fun_t *>(nullptr)',
'(fun_t *) nullptr'),
- ('new tmwa::magic::fun_t{"name"_s, "sig"_s, \'\\0\', nullptr}',
- 'regex:\(fun_t \*\) = \{->name = "name", ->signature = "sig", ->ret_ty = 0 \'\\\\000\', ->fun = (0x)?0}'),
+ ('new tmwa::map::magic::fun_t{"name"_s, "sig"_s, \'\\0\', nullptr}',
+ '(fun_t *) = {->name = "name", ->signature = "sig", ->ret_ty = 0 \'\\000\', ->fun = nullptr}'),
]
diff --git a/src/map/magic-interpreter-base.cpp b/src/map/magic-interpreter-base.cpp
index be9a61a..c2be363 100644
--- a/src/map/magic-interpreter-base.cpp
+++ b/src/map/magic-interpreter-base.cpp
@@ -25,10 +25,12 @@
#include "../strings/xstring.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
+
+#include "../mmo/cxxstdio_enums.hpp"
#include "../net/timer.hpp"
+#include "globals.hpp"
#include "magic.hpp"
#include "magic-expr.hpp"
#include "magic-interpreter.hpp"
@@ -39,6 +41,8 @@
namespace tmwa
{
+namespace map
+{
namespace magic
{
static
@@ -78,9 +82,6 @@ void set_spell(val_t *v, dumb_ptr<spell_t> x)
*v = ValSpell{x};
}
-magic_conf_t magic_conf; /* Global magic conf */
-env_t magic_default_env = { &magic_conf, nullptr };
-
AString magic_find_invocation(XString spellname)
{
auto it = magic_conf.spells_by_name.find(spellname);
@@ -313,22 +314,22 @@ const effect_set_t *spellguard_check_sub(spellguard_check_t *check,
if (guard == nullptr)
return nullptr;
- MATCH (*guard)
+ MATCH_BEGIN (*guard)
{
- CASE (const GuardCondition&, s)
+ MATCH_CASE (const GuardCondition&, s)
{
if (!magic_eval_int(env, s.s_condition))
return nullptr;
}
- CASE (const GuardComponents&, s)
+ MATCH_CASE (const GuardComponents&, s)
{
copy_components(&check->components, s.s_components);
}
- CASE (const GuardCatalysts&, s)
+ MATCH_CASE (const GuardCatalysts&, s)
{
copy_components(&check->catalysts, s.s_catalysts);
}
- CASE (const GuardChoice&, s)
+ MATCH_CASE (const GuardChoice&, s)
{
spellguard_check_t altcheck = *check;
const effect_set_t *retval;
@@ -350,15 +351,15 @@ const effect_set_t *spellguard_check_sub(spellguard_check_t *check,
return spellguard_check_sub(check, s.s_alt, caster,
env, near_miss);
}
- CASE (const GuardMana&, s)
+ MATCH_CASE (const GuardMana&, s)
{
check->mana += magic_eval_int(env, s.s_mana);
}
- CASE (const GuardCastTime&, s)
+ MATCH_CASE (const GuardCastTime&, s)
{
check->casttime += static_cast<interval_t>(magic_eval_int(env, s.s_casttime));
}
- CASE (const effect_set_t&, s_effect)
+ MATCH_CASE (const effect_set_t&, s_effect)
{
if (spellguard_can_satisfy(check, caster, env, near_miss))
return &s_effect;
@@ -366,6 +367,7 @@ const effect_set_t *spellguard_check_sub(spellguard_check_t *check,
return nullptr;
}
}
+ MATCH_END ();
return spellguard_check_sub(check, guard->next, caster, env, near_miss);
}
@@ -547,4 +549,5 @@ int spell_unbind(dumb_ptr<map_session_data> subject, dumb_ptr<invocation> invoca
return 1;
}
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic-interpreter-base.hpp b/src/map/magic-interpreter-base.hpp
index 4bb41a0..7c00db0 100644
--- a/src/map/magic-interpreter-base.hpp
+++ b/src/map/magic-interpreter-base.hpp
@@ -21,20 +21,13 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
-#include "../generic/fwd.hpp"
-
-#include "../mmo/fwd.hpp"
-
namespace tmwa
{
+namespace map
+{
namespace magic
{
-extern magic_conf_t magic_conf; /* Global magic conf */
-extern env_t magic_default_env; /* Fake default environment */
-
/**
* Adds a component selection to a component holder (which may initially be nullptr)
*/
@@ -87,4 +80,5 @@ dumb_ptr<spell_t> magic_find_spell(XString invocation);
void spell_update_location(dumb_ptr<invocation> invocation);
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic-interpreter.hpp b/src/map/magic-interpreter.hpp
index 3bb600c..cbd92a9 100644
--- a/src/map/magic-interpreter.hpp
+++ b/src/map/magic-interpreter.hpp
@@ -19,39 +19,43 @@
// 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 "fwd.hpp"
-
#include "magic-interpreter.t.hpp"
+#include "fwd.hpp"
+
#include <cassert>
#include <memory>
-#include "../strings/fwd.hpp"
#include "../strings/rstring.hpp"
-#include "../generic/fwd.hpp"
-
#include "../sexpr/variant.hpp"
#include "../net/timer.t.hpp"
#include "../mmo/ids.hpp"
-#include "../mmo/utils.hpp"
#include "map.hpp"
-#include "script.hpp"
-#include "skill.t.hpp"
+#include "script-buffer.hpp"
+#include "../mmo/skill.t.hpp"
namespace tmwa
{
+namespace map
+{
namespace magic
{
struct location_t
{
- map_local *m;
+ Borrowed<map_local> m;
int x, y;
+
+ // This constructor exists solely to work around the design constraints
+ // of sexpr::Variant<>. See comments in variant.tcc for future plans.
+ __attribute__((deprecated))
+ location_t() noexcept : m(borrow(undefined_gat)), x(), y() {}
+ location_t(Borrowed<map_local> m_, int x_, int y_) : m(m_), x(x_), y(y_) {}
};
struct AreaUnion
@@ -622,4 +626,5 @@ struct proc_t
{}
};
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic-interpreter.py b/src/map/magic-interpreter.py
index cf17b1c..520ab37 100644
--- a/src/map/magic-interpreter.py
+++ b/src/map/magic-interpreter.py
@@ -1,6 +1,6 @@
class AreaUnion(object):
__slots__ = ('_value')
- name = 'tmwa::magic::AreaUnion'
+ name = 'tmwa::map::magic::AreaUnion'
enabled = True
def __init__(self, value):
@@ -27,23 +27,23 @@ class area_t(object):
using tmwa::operator "" _s;
inline
- tmwa::map_local *fake_map_local_x_dup_for_area_t(tmwa::ZString name)
+ tmwa::Borrowed<tmwa::map::map_local> fake_map_local_x_dup_for_area_t(tmwa::ZString name)
{
- auto *p = new tmwa::map_local{};
+ auto *p = new tmwa::map::map_local{};
p->name_ = tmwa::stringish<tmwa::MapName>(name);
- return p;
+ return tmwa::borrow(*p);
}
'''
tests = [
- ('tmwa::magic::area_t(tmwa::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456})',
- '{<tmwa::sexpr::Variant<tmwa::magic::location_t, tmwa::magic::AreaUnion, tmwa::magic::AreaRect, tmwa::magic::AreaBar>> = {(tmwa::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}}, size = 1}'),
- ('tmwa::magic::area_t(tmwa::magic::AreaUnion{{tmwa::dumb_ptr<tmwa::magic::area_t>::make(tmwa::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456}), tmwa::dumb_ptr<tmwa::magic::area_t>::make(tmwa::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 321, 654})}})',
- '{<tmwa::sexpr::Variant<tmwa::magic::location_t, tmwa::magic::AreaUnion, tmwa::magic::AreaRect, tmwa::magic::AreaBar>> = {(tmwa::magic::AreaUnion) = {{<tmwa::sexpr::Variant<tmwa::magic::location_t, tmwa::magic::AreaUnion, tmwa::magic::AreaRect, tmwa::magic::AreaBar>> = {(tmwa::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}}, size = 1}, {<tmwa::sexpr::Variant<tmwa::magic::location_t, tmwa::magic::AreaUnion, tmwa::magic::AreaRect, tmwa::magic::AreaBar>> = {(tmwa::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 321, y = 654}}, size = 1}}}, size = 2}'),
- ('tmwa::magic::area_t(tmwa::magic::AreaRect{tmwa::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456}, 789, 102})',
- '{<tmwa::sexpr::Variant<tmwa::magic::location_t, tmwa::magic::AreaUnion, tmwa::magic::AreaRect, tmwa::magic::AreaBar>> = {(tmwa::magic::AreaRect) = {loc = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}, width = 789, height = 102}}, size = 80478}'),
- ('tmwa::magic::area_t(tmwa::magic::AreaBar{tmwa::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 42, 43}, 123, 456, tmwa::DIR::NW})',
- '{<tmwa::sexpr::Variant<tmwa::magic::location_t, tmwa::magic::AreaUnion, tmwa::magic::AreaRect, tmwa::magic::AreaBar>> = {(tmwa::magic::AreaBar) = {loc = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 42, y = 43}, width = 123, depth = 456, dir = tmwa::DIR::NW}}, size = 112632}'),
+ ('tmwa::map::magic::area_t(tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::location_t, tmwa::map::magic::AreaUnion, tmwa::map::magic::AreaRect, tmwa::map::magic::AreaBar>> = {(tmwa::map::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}}, size = 1}'),
+ ('tmwa::map::magic::area_t(tmwa::map::magic::AreaUnion{{tmwa::dumb_ptr<tmwa::map::magic::area_t>::make(tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456}), tmwa::dumb_ptr<tmwa::map::magic::area_t>::make(tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 321, 654})}})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::location_t, tmwa::map::magic::AreaUnion, tmwa::map::magic::AreaRect, tmwa::map::magic::AreaBar>> = {(tmwa::map::magic::AreaUnion) = {{<tmwa::sexpr::Variant<tmwa::map::magic::location_t, tmwa::map::magic::AreaUnion, tmwa::map::magic::AreaRect, tmwa::map::magic::AreaBar>> = {(tmwa::map::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}}, size = 1}, {<tmwa::sexpr::Variant<tmwa::map::magic::location_t, tmwa::map::magic::AreaUnion, tmwa::map::magic::AreaRect, tmwa::map::magic::AreaBar>> = {(tmwa::map::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 321, y = 654}}, size = 1}}}, size = 2}'),
+ ('tmwa::map::magic::area_t(tmwa::map::magic::AreaRect{tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456}, 789, 102})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::location_t, tmwa::map::magic::AreaUnion, tmwa::map::magic::AreaRect, tmwa::map::magic::AreaBar>> = {(tmwa::map::magic::AreaRect) = {loc = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}, width = 789, height = 102}}, size = 80478}'),
+ ('tmwa::map::magic::area_t(tmwa::map::magic::AreaBar{tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 42, 43}, 123, 456, tmwa::DIR::NW})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::location_t, tmwa::map::magic::AreaUnion, tmwa::map::magic::AreaRect, tmwa::map::magic::AreaBar>> = {(tmwa::map::magic::AreaBar) = {loc = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 42, y = 43}, width = 123, depth = 456, dir = tmwa::DIR::NW}}, size = 112632}'),
]
@@ -55,47 +55,47 @@ class val_t(object):
using tmwa::operator "" _s;
inline
- tmwa::map_local *fake_map_local_x_dup_for_val_t(tmwa::ZString name)
+ tmwa::Borrowed<tmwa::map::map_local> fake_map_local_x_dup_for_val_t(tmwa::ZString name)
{
- auto *p = new tmwa::map_local{};
+ auto *p = new tmwa::map::map_local{};
p->name_ = tmwa::stringish<tmwa::MapName>(name);
- return p;
+ return tmwa::borrow(*p);
}
'''
tests = [
- ('tmwa::magic::val_t(tmwa::magic::ValUndef{})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValUndef) = {<No data fields>}}, <No data fields>}'),
- ('tmwa::magic::val_t(tmwa::magic::ValInt{42})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValInt) = {v_int = 42}}, <No data fields>}'),
- ('tmwa::magic::val_t(tmwa::magic::ValDir{tmwa::DIR::NW})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValDir) = {v_dir = tmwa::DIR::NW}}, <No data fields>}'),
- ('tmwa::magic::val_t(tmwa::magic::ValString{"Hello"_s})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValString) = {v_string = "Hello"}}, <No data fields>}'),
- ('tmwa::magic::val_t(tmwa::magic::ValEntityInt{tmwa::wrap<tmwa::BlockId>(123)})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValEntityInt) = {v_eid = 123}}, <No data fields>}'),
- ('tmwa::magic::val_t(tmwa::magic::ValEntityPtr{tmwa::dumb_ptr<tmwa::block_list>()})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValEntityPtr) = {v_entity = 0x0}}, <No data fields>}'),
- ('tmwa::magic::val_t(tmwa::magic::ValLocation{tmwa::magic::location_t{fake_map_local_x_dup_for_val_t("map"_s), 42, 123}})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValLocation) = {v_location = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 42, y = 123}}}, <No data fields>}'),
- ('tmwa::magic::val_t(tmwa::magic::ValArea{tmwa::dumb_ptr<tmwa::magic::area_t>()})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValArea) = {v_area = 0x0}}, <No data fields>}'),
- ('tmwa::magic::val_t(tmwa::magic::ValSpell{tmwa::dumb_ptr<tmwa::magic::spell_t>()})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValSpell) = {v_spell = 0x0}}, <No data fields>}'),
- ('tmwa::magic::val_t(tmwa::magic::ValInvocationInt{tmwa::wrap<tmwa::BlockId>(123)})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValInvocationInt) = {v_iid = 123}}, <No data fields>}'),
- ('tmwa::magic::val_t(tmwa::magic::ValInvocationPtr{})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValInvocationPtr) = {v_invocation = 0x0}}, <No data fields>}'),
- ('tmwa::magic::val_t(tmwa::magic::ValFail{})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValFail) = {<No data fields>}}, <No data fields>}'),
- ('tmwa::magic::val_t(tmwa::magic::ValNegative1{})',
- '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValNegative1) = {<No data fields>}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValUndef{})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValUndef) = {<No data fields>}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValInt{42})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValInt) = {v_int = 42}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValDir{tmwa::DIR::NW})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValDir) = {v_dir = tmwa::DIR::NW}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValString{"Hello"_s})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValString) = {v_string = "Hello"}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValEntityInt{tmwa::wrap<tmwa::BlockId>(123)})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValEntityInt) = {v_eid = 123}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValEntityPtr{tmwa::dumb_ptr<tmwa::map::block_list>()})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValEntityPtr) = {v_entity = 0x0}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValLocation{tmwa::map::magic::location_t{fake_map_local_x_dup_for_val_t("map"_s), 42, 123}})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValLocation) = {v_location = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 42, y = 123}}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValArea{tmwa::dumb_ptr<tmwa::map::magic::area_t>()})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValArea) = {v_area = 0x0}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValSpell{tmwa::dumb_ptr<tmwa::map::magic::spell_t>()})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValSpell) = {v_spell = 0x0}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValInvocationInt{tmwa::wrap<tmwa::BlockId>(123)})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValInvocationInt) = {v_iid = 123}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValInvocationPtr{})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValInvocationPtr) = {v_invocation = 0x0}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValFail{})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValFail) = {<No data fields>}}, <No data fields>}'),
+ ('tmwa::map::magic::val_t(tmwa::map::magic::ValNegative1{})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValNegative1) = {<No data fields>}}, <No data fields>}'),
]
class ExprAreaUnion(object):
__slots__ = ('_value')
- name = 'tmwa::magic::ExprAreaUnion'
+ name = 'tmwa::map::magic::ExprAreaUnion'
enabled = True
def __init__(self, value):
@@ -119,14 +119,14 @@ class e_area_t(object):
enabled = True
tests = [
- ('tmwa::magic::e_area_t(tmwa::magic::e_location_t())',
- '{<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}'),
- ('tmwa::magic::e_area_t(tmwa::magic::ExprAreaUnion{{tmwa::dumb_ptr<tmwa::magic::e_area_t>::make(tmwa::magic::e_location_t()), tmwa::dumb_ptr<tmwa::magic::e_area_t>::make(tmwa::magic::e_location_t())}})',
- '{<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::ExprAreaUnion) = {{<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}, {<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}}}, <No data fields>}'),
- ('tmwa::magic::e_area_t(tmwa::magic::ExprAreaRect{tmwa::magic::e_location_t(), tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::expr_t>()})',
- '{<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::ExprAreaRect) = {loc = {m = 0x0, x = 0x0, y = 0x0}, width = 0x0, height = 0x0}}, <No data fields>}'),
- ('tmwa::magic::e_area_t(tmwa::magic::ExprAreaBar{tmwa::magic::e_location_t(), tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::expr_t>()})',
- '{<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::ExprAreaBar) = {loc = {m = 0x0, x = 0x0, y = 0x0}, width = 0x0, depth = 0x0, dir = 0x0}}, <No data fields>}'),
+ ('tmwa::map::magic::e_area_t(tmwa::map::magic::e_location_t())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}'),
+ ('tmwa::map::magic::e_area_t(tmwa::map::magic::ExprAreaUnion{{tmwa::dumb_ptr<tmwa::map::magic::e_area_t>::make(tmwa::map::magic::e_location_t()), tmwa::dumb_ptr<tmwa::map::magic::e_area_t>::make(tmwa::map::magic::e_location_t())}})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::ExprAreaUnion) = {{<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}, {<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}}}, <No data fields>}'),
+ ('tmwa::map::magic::e_area_t(tmwa::map::magic::ExprAreaRect{tmwa::map::magic::e_location_t(), tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::expr_t>()})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::ExprAreaRect) = {loc = {m = 0x0, x = 0x0, y = 0x0}, width = 0x0, height = 0x0}}, <No data fields>}'),
+ ('tmwa::map::magic::e_area_t(tmwa::map::magic::ExprAreaBar{tmwa::map::magic::e_location_t(), tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::expr_t>()})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::ExprAreaBar) = {loc = {m = 0x0, x = 0x0, y = 0x0}, width = 0x0, depth = 0x0, dir = 0x0}}, <No data fields>}'),
]
@@ -135,18 +135,18 @@ class expr_t(object):
enabled = True
tests = [
- ('tmwa::magic::expr_t(tmwa::magic::val_t(tmwa::magic::ValUndef()))',
- '{<tmwa::sexpr::Variant<tmwa::magic::val_t, tmwa::magic::e_location_t, tmwa::magic::e_area_t, tmwa::magic::ExprFunApp, tmwa::magic::ExprId, tmwa::magic::ExprField>> = {(tmwa::magic::val_t) = {<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValUndef) = {<No data fields>}}, <No data fields>}}, <No data fields>}'),
- ('tmwa::magic::expr_t(tmwa::magic::e_location_t())',
- '{<tmwa::sexpr::Variant<tmwa::magic::val_t, tmwa::magic::e_location_t, tmwa::magic::e_area_t, tmwa::magic::ExprFunApp, tmwa::magic::ExprId, tmwa::magic::ExprField>> = {(tmwa::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}'),
- ('tmwa::magic::expr_t(tmwa::magic::e_area_t(tmwa::magic::e_location_t()))',
- '{<tmwa::sexpr::Variant<tmwa::magic::val_t, tmwa::magic::e_location_t, tmwa::magic::e_area_t, tmwa::magic::ExprFunApp, tmwa::magic::ExprId, tmwa::magic::ExprField>> = {(tmwa::magic::e_area_t) = {<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}}, <No data fields>}'),
- ('tmwa::magic::expr_t(tmwa::magic::ExprFunApp())',
- '{<tmwa::sexpr::Variant<tmwa::magic::val_t, tmwa::magic::e_location_t, tmwa::magic::e_area_t, tmwa::magic::ExprFunApp, tmwa::magic::ExprId, tmwa::magic::ExprField>> = {(tmwa::magic::ExprFunApp) = {funp = (fun_t *) nullptr, line_nr = 0, column = 0, args_nr = 0, args = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, <No data fields>}'),
- ('tmwa::magic::expr_t(tmwa::magic::ExprId{123})',
- '{<tmwa::sexpr::Variant<tmwa::magic::val_t, tmwa::magic::e_location_t, tmwa::magic::e_area_t, tmwa::magic::ExprFunApp, tmwa::magic::ExprId, tmwa::magic::ExprField>> = {(tmwa::magic::ExprId) = {e_id = 123}}, <No data fields>}'),
- ('tmwa::magic::expr_t(tmwa::magic::ExprField{tmwa::dumb_ptr<tmwa::magic::expr_t>(), 42})',
- '{<tmwa::sexpr::Variant<tmwa::magic::val_t, tmwa::magic::e_location_t, tmwa::magic::e_area_t, tmwa::magic::ExprFunApp, tmwa::magic::ExprId, tmwa::magic::ExprField>> = {(tmwa::magic::ExprField) = {expr = 0x0, id = 42}}, <No data fields>}'),
+ ('tmwa::map::magic::expr_t(tmwa::map::magic::val_t(tmwa::map::magic::ValUndef()))',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::val_t, tmwa::map::magic::e_location_t, tmwa::map::magic::e_area_t, tmwa::map::magic::ExprFunApp, tmwa::map::magic::ExprId, tmwa::map::magic::ExprField>> = {(tmwa::map::magic::val_t) = {<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValUndef) = {<No data fields>}}, <No data fields>}}, <No data fields>}'),
+ ('tmwa::map::magic::expr_t(tmwa::map::magic::e_location_t())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::val_t, tmwa::map::magic::e_location_t, tmwa::map::magic::e_area_t, tmwa::map::magic::ExprFunApp, tmwa::map::magic::ExprId, tmwa::map::magic::ExprField>> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}'),
+ ('tmwa::map::magic::expr_t(tmwa::map::magic::e_area_t(tmwa::map::magic::e_location_t()))',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::val_t, tmwa::map::magic::e_location_t, tmwa::map::magic::e_area_t, tmwa::map::magic::ExprFunApp, tmwa::map::magic::ExprId, tmwa::map::magic::ExprField>> = {(tmwa::map::magic::e_area_t) = {<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}}, <No data fields>}'),
+ ('tmwa::map::magic::expr_t(tmwa::map::magic::ExprFunApp())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::val_t, tmwa::map::magic::e_location_t, tmwa::map::magic::e_area_t, tmwa::map::magic::ExprFunApp, tmwa::map::magic::ExprId, tmwa::map::magic::ExprField>> = {(tmwa::map::magic::ExprFunApp) = {funp = (fun_t *) nullptr, line_nr = 0, column = 0, args_nr = 0, args = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, <No data fields>}'),
+ ('tmwa::map::magic::expr_t(tmwa::map::magic::ExprId{123})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::val_t, tmwa::map::magic::e_location_t, tmwa::map::magic::e_area_t, tmwa::map::magic::ExprFunApp, tmwa::map::magic::ExprId, tmwa::map::magic::ExprField>> = {(tmwa::map::magic::ExprId) = {e_id = 123}}, <No data fields>}'),
+ ('tmwa::map::magic::expr_t(tmwa::map::magic::ExprField{tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), 42})',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::val_t, tmwa::map::magic::e_location_t, tmwa::map::magic::e_area_t, tmwa::map::magic::ExprFunApp, tmwa::map::magic::ExprId, tmwa::map::magic::ExprField>> = {(tmwa::map::magic::ExprField) = {expr = 0x0, id = 42}}, <No data fields>}'),
]
@@ -154,30 +154,30 @@ class effect_t(object):
enabled = True
tests = [
- ('tmwa::magic::effect_t(tmwa::magic::EffectSkip{}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectSkip) = {<No data fields>}}, next = 0x0}'),
- ('tmwa::magic::effect_t(tmwa::magic::EffectAbort{}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectAbort) = {<No data fields>}}, next = 0x0}'),
- ('tmwa::magic::effect_t(tmwa::magic::EffectAssign{42, tmwa::dumb_ptr<tmwa::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectAssign) = {id = 42, expr = 0x0}}, next = 0x0}'),
- ('tmwa::magic::effect_t(tmwa::magic::EffectForEach{123, tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::effect_t>(), tmwa::magic::FOREACH_FILTER::PC}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectForEach) = {id = 123, area = 0x0, body = 0x0, filter = tmwa::magic::FOREACH_FILTER::PC}}, next = 0x0}'),
- ('tmwa::magic::effect_t(tmwa::magic::EffectFor{42, tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectFor) = {id = 42, start = 0x0, stop = 0x0, body = 0x0}}, next = 0x0}'),
- ('tmwa::magic::effect_t(tmwa::magic::EffectIf{tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::effect_t>(), tmwa::dumb_ptr<tmwa::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectIf) = {cond = 0x0, true_branch = 0x0, false_branch = 0x0}}, next = 0x0}'),
- ('tmwa::magic::effect_t(tmwa::magic::EffectSleep{tmwa::dumb_ptr<tmwa::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectSleep) = {e_sleep = 0x0}}, next = 0x0}'),
- ('tmwa::magic::effect_t(tmwa::magic::EffectScript{tmwa::dumb_ptr<const tmwa::ScriptBuffer>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectScript) = {e_script = 0x0}}, next = 0x0}'),
- ('tmwa::magic::effect_t(tmwa::magic::EffectBreak{}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectBreak) = {<No data fields>}}, next = 0x0}'),
- ('tmwa::magic::effect_t(tmwa::magic::EffectOp(), tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectOp) = {opp = (op_t *) nullptr, args_nr = 0, line_nr = 0, column = 0, args = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, next = 0x0}'),
- ('tmwa::magic::effect_t(tmwa::magic::EffectEnd{}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectEnd) = {<No data fields>}}, next = 0x0}'),
- ('tmwa::magic::effect_t(tmwa::magic::EffectCall{nullptr, tmwa::dumb_ptr<std::vector<tmwa::dumb_ptr<tmwa::magic::expr_t>>>(), tmwa::dumb_ptr<tmwa::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectCall) = {formalv = 0x0, actualvp = 0x0, body = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectSkip{}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectSkip) = {<No data fields>}}, next = 0x0}'),
+ ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectAbort{}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectAbort) = {<No data fields>}}, next = 0x0}'),
+ ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectAssign{42, tmwa::dumb_ptr<tmwa::map::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectAssign) = {id = 42, expr = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectForEach{123, tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>(), tmwa::map::magic::FOREACH_FILTER::PC}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectForEach) = {id = 123, area = 0x0, body = 0x0, filter = tmwa::map::magic::FOREACH_FILTER::PC}}, next = 0x0}'),
+ ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectFor{42, tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectFor) = {id = 42, start = 0x0, stop = 0x0, body = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectIf{tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectIf) = {cond = 0x0, true_branch = 0x0, false_branch = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectSleep{tmwa::dumb_ptr<tmwa::map::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectSleep) = {e_sleep = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectScript{tmwa::dumb_ptr<const tmwa::map::ScriptBuffer>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectScript) = {e_script = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectBreak{}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectBreak) = {<No data fields>}}, next = 0x0}'),
+ ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectOp(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectOp) = {opp = (op_t *) nullptr, args_nr = 0, line_nr = 0, column = 0, args = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, next = 0x0}'),
+ ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectEnd{}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectEnd) = {<No data fields>}}, next = 0x0}'),
+ ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectCall{nullptr, tmwa::dumb_ptr<std::vector<tmwa::dumb_ptr<tmwa::map::magic::expr_t>>>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectCall) = {formalv = nullptr, actualvp = 0x0, body = 0x0}}, next = 0x0}'),
]
@@ -185,20 +185,20 @@ class spellguard_t(object):
enabled = True
tests = [
- ('tmwa::magic::spellguard_t(tmwa::magic::GuardCondition{tmwa::dumb_ptr<tmwa::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::GuardCondition) = {s_condition = 0x0}}, next = 0x0}'),
- ('tmwa::magic::spellguard_t(tmwa::magic::GuardMana{tmwa::dumb_ptr<tmwa::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::GuardMana) = {s_mana = 0x0}}, next = 0x0}'),
- ('tmwa::magic::spellguard_t(tmwa::magic::GuardCastTime{tmwa::dumb_ptr<tmwa::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::GuardCastTime) = {s_casttime = 0x0}}, next = 0x0}'),
- ('tmwa::magic::spellguard_t(tmwa::magic::GuardComponents{tmwa::dumb_ptr<tmwa::magic::component_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::GuardComponents) = {s_components = 0x0}}, next = 0x0}'),
- ('tmwa::magic::spellguard_t(tmwa::magic::GuardCatalysts{tmwa::dumb_ptr<tmwa::magic::component_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::GuardCatalysts) = {s_catalysts = 0x0}}, next = 0x0}'),
- ('tmwa::magic::spellguard_t(tmwa::magic::GuardChoice{tmwa::dumb_ptr<tmwa::magic::spellguard_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::GuardChoice) = {s_alt = 0x0}}, next = 0x0}'),
- ('tmwa::magic::spellguard_t(tmwa::magic::effect_set_t{tmwa::dumb_ptr<tmwa::magic::effect_t>(), tmwa::dumb_ptr<tmwa::magic::effect_t>(), tmwa::dumb_ptr<tmwa::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::effect_set_t) = {effect = 0x0, at_trigger = 0x0, at_end = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardCondition{tmwa::dumb_ptr<tmwa::map::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::GuardCondition) = {s_condition = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardMana{tmwa::dumb_ptr<tmwa::map::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::GuardMana) = {s_mana = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardCastTime{tmwa::dumb_ptr<tmwa::map::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::GuardCastTime) = {s_casttime = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardComponents{tmwa::dumb_ptr<tmwa::map::magic::component_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::GuardComponents) = {s_components = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardCatalysts{tmwa::dumb_ptr<tmwa::map::magic::component_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::GuardCatalysts) = {s_catalysts = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardChoice{tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::GuardChoice) = {s_alt = 0x0}}, next = 0x0}'),
+ ('tmwa::map::magic::spellguard_t(tmwa::map::magic::effect_set_t{tmwa::dumb_ptr<tmwa::map::magic::effect_t>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::effect_set_t) = {effect = 0x0, at_trigger = 0x0, at_end = 0x0}}, next = 0x0}'),
]
@@ -206,10 +206,10 @@ class cont_activation_record_t(object):
enabled = True
tests = [
- ('tmwa::magic::cont_activation_record_t(tmwa::magic::CarForEach{42, true, tmwa::dumb_ptr<tmwa::magic::effect_t>(), tmwa::dumb_ptr<std::vector<tmwa::BlockId>>(), 123}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::CarForEach, tmwa::magic::CarFor, tmwa::magic::CarProc>> = {(tmwa::magic::CarForEach) = {id = 42, ty_is_spell_not_entity = true, body = 0x0, entities_vp = 0x0, index = 123}}, return_location = 0x0}'),
- ('tmwa::magic::cont_activation_record_t(tmwa::magic::CarFor{42, tmwa::dumb_ptr<tmwa::magic::effect_t>(), 123, 456}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::CarForEach, tmwa::magic::CarFor, tmwa::magic::CarProc>> = {(tmwa::magic::CarFor) = {id = 42, body = 0x0, current = 123, stop = 456}}, return_location = 0x0}'),
- ('tmwa::magic::cont_activation_record_t(tmwa::magic::CarProc{123, nullptr, tmwa::dumb_ptr<tmwa::magic::val_t[]>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())',
- '{<tmwa::sexpr::Variant<tmwa::magic::CarForEach, tmwa::magic::CarFor, tmwa::magic::CarProc>> = {(tmwa::magic::CarProc) = {args_nr = 123, formalap = 0x0, old_actualpa = 0x0 = {sz = 0}}}, return_location = 0x0}'),
+ ('tmwa::map::magic::cont_activation_record_t(tmwa::map::magic::CarForEach{42, true, tmwa::dumb_ptr<tmwa::map::magic::effect_t>(), tmwa::dumb_ptr<std::vector<tmwa::BlockId>>(), 123}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::CarForEach, tmwa::map::magic::CarFor, tmwa::map::magic::CarProc>> = {(tmwa::map::magic::CarForEach) = {id = 42, ty_is_spell_not_entity = true, body = 0x0, entities_vp = 0x0, index = 123}}, return_location = 0x0}'),
+ ('tmwa::map::magic::cont_activation_record_t(tmwa::map::magic::CarFor{42, tmwa::dumb_ptr<tmwa::map::magic::effect_t>(), 123, 456}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::CarForEach, tmwa::map::magic::CarFor, tmwa::map::magic::CarProc>> = {(tmwa::map::magic::CarFor) = {id = 42, body = 0x0, current = 123, stop = 456}}, return_location = 0x0}'),
+ ('tmwa::map::magic::cont_activation_record_t(tmwa::map::magic::CarProc{123, nullptr, tmwa::dumb_ptr<tmwa::map::magic::val_t[]>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())',
+ '{<tmwa::sexpr::Variant<tmwa::map::magic::CarForEach, tmwa::map::magic::CarFor, tmwa::map::magic::CarProc>> = {(tmwa::map::magic::CarProc) = {args_nr = 123, formalap = nullptr, old_actualpa = 0x0 = {sz = 0}}}, return_location = 0x0}'),
]
diff --git a/src/map/magic-interpreter.t.hpp b/src/map/magic-interpreter.t.hpp
index ab151fc..e302354 100644
--- a/src/map/magic-interpreter.t.hpp
+++ b/src/map/magic-interpreter.t.hpp
@@ -26,6 +26,8 @@
namespace tmwa
{
+namespace map
+{
namespace magic
{
enum class SPELLARG : uint8_t
@@ -79,4 +81,5 @@ ENUM_BITWISE_OPERATORS(INVOCATION_FLAG)
}
using e::INVOCATION_FLAG;
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic-stmt.cpp b/src/map/magic-stmt.cpp
index 2a657fa..4d8330a 100644
--- a/src/map/magic-stmt.cpp
+++ b/src/map/magic-stmt.cpp
@@ -29,7 +29,8 @@
#include "../generic/random2.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
+
+#include "../mmo/cxxstdio_enums.hpp"
#include "../net/timer.hpp"
@@ -42,7 +43,9 @@
#include "magic-interpreter-base.hpp"
#include "mob.hpp"
#include "npc.hpp"
+#include "npc-parse.hpp"
#include "pc.hpp"
+#include "script-call.hpp"
#include "skill.hpp"
#include "../poison.hpp"
@@ -50,6 +53,8 @@
namespace tmwa
{
+namespace map
+{
namespace magic
{
/* used for local spell effects */
@@ -58,17 +63,18 @@ constexpr Species INVISIBLE_NPC = wrap<Species>(127);
static
void clear_activation_record(cont_activation_record_t *ar)
{
- MATCH (*ar)
+ MATCH_BEGIN (*ar)
{
- CASE (CarForEach&, c_foreach)
+ MATCH_CASE (CarForEach&, c_foreach)
{
c_foreach.entities_vp.delete_();
}
- CASE (CarProc&, c_proc)
+ MATCH_CASE (CarProc&, c_proc)
{
c_proc.old_actualpa.delete_();
}
}
+ MATCH_END ();
}
static
@@ -218,7 +224,7 @@ BlockId trigger_spell(BlockId subject, BlockId spell)
}
static
-void entity_warp(dumb_ptr<block_list> target, map_local *destm, int destx, int desty);
+void entity_warp(dumb_ptr<block_list> target, Borrowed<map_local> destm, int destx, int desty);
static
void char_update(dumb_ptr<map_session_data> character)
@@ -261,7 +267,7 @@ void timer_callback_effect_npc_delete(TimerData *, tick_t, BlockId npc_id)
}
static
-dumb_ptr<npc_data> local_spell_effect(map_local *m, int x, int y, int effect,
+dumb_ptr<npc_data> local_spell_effect(Borrowed<map_local> m, int x, int y, int effect,
interval_t tdelay)
{
/* 1 minute should be enough for all interesting spell effects, I hope */
@@ -421,7 +427,7 @@ int op_messenger_npc(dumb_ptr<env_t>, Slice<val_t> args)
}
static
-void entity_warp(dumb_ptr<block_list> target, map_local *destm, int destx, int desty)
+void entity_warp(dumb_ptr<block_list> target, Borrowed<map_local> destm, int destx, int desty)
{
if (target->bl_type == BL::PC || target->bl_type == BL::MOB)
{
@@ -767,8 +773,8 @@ int op_injure(dumb_ptr<env_t> env, Slice<val_t> args)
if (target->bl_type == BL::PC
&& !target->bl_m->flag.get(MapFlag::PVP)
- && !target->is_player()->special_state.killable
- && (caster->bl_type != BL::PC || !caster->is_player()->special_state.killer))
+ && (caster->bl_type == BL::PC)
+ && ((caster->is_player()->state.pvpchannel > 1) && (target->is_player()->state.pvpchannel != caster->is_player()->state.pvpchannel)))
return 0; /* Cannot damage other players outside of pvp */
if (target != caster)
@@ -786,7 +792,7 @@ int op_injure(dumb_ptr<env_t> env, Slice<val_t> args)
// display damage first, because dealing damage may deallocate the target.
clif_damage(caster, target,
gettick(), interval_t::zero(), interval_t::zero(),
- damage_caused, 0, DamageType::NORMAL, 0);
+ damage_caused, 0, DamageType::NORMAL);
if (caster->bl_type == BL::PC)
{
@@ -995,9 +1001,9 @@ dumb_ptr<effect_t> return_to_stack(dumb_ptr<invocation> invocation_)
{
cont_activation_record_t *ar =
&invocation_->stack.back();
- MATCH (*ar)
+ MATCH_BEGIN (*ar)
{
- CASE (const CarProc&, c_proc)
+ MATCH_CASE (const CarProc&, c_proc)
{
dumb_ptr<effect_t> ret = ar->return_location;
for (int i = 0; i < c_proc.args_nr; i++)
@@ -1014,7 +1020,7 @@ dumb_ptr<effect_t> return_to_stack(dumb_ptr<invocation> invocation_)
return ret;
}
- CASE (CarForEach&, c_foreach)
+ MATCH_CASE (CarForEach&, c_foreach)
{
BlockId entity_id;
val_t *var = &invocation_->env->varu[c_foreach.id];
@@ -1045,7 +1051,7 @@ dumb_ptr<effect_t> return_to_stack(dumb_ptr<invocation> invocation_)
return c_foreach.body;
}
- CASE (CarFor&, c_for)
+ MATCH_CASE (CarFor&, c_for)
{
if (c_for.current > c_for.stop)
{
@@ -1062,6 +1068,7 @@ dumb_ptr<effect_t> return_to_stack(dumb_ptr<invocation> invocation_)
return c_for.body;
}
}
+ MATCH_END ();
abort();
}
}
@@ -1133,46 +1140,43 @@ void find_entities_in_area(area_t& area_,
std::vector<BlockId> *entities_vp,
FOREACH_FILTER filter)
{
- MATCH (area_)
+ MATCH_BEGIN (area_)
{
- CASE (const AreaUnion&, a)
+ MATCH_CASE (const AreaUnion&, a)
{
find_entities_in_area(*a.a_union[0], entities_vp, filter);
find_entities_in_area(*a.a_union[1], entities_vp, filter);
}
- CASE (const location_t&, a_loc)
+ MATCH_CASE (const location_t&, a_loc)
{
(void)a_loc;
// TODO this can be simplified
- map_local *m;
int x, y, width, height;
- magic_area_rect(&m, &x, &y, &width, &height, area_);
+ Borrowed<map_local> m = magic_area_rect(&x, &y, &width, &height, area_);
map_foreachinarea(std::bind(find_entities_in_area_c, ph::_1, entities_vp, filter),
m,
x, y,
x + width, y + height,
BL::NUL /* filter elsewhere */);
}
- CASE (const AreaRect&, a_rect)
+ MATCH_CASE (const AreaRect&, a_rect)
{
(void)a_rect;
// TODO this can be simplified
- map_local *m;
int x, y, width, height;
- magic_area_rect(&m, &x, &y, &width, &height, area_);
+ Borrowed<map_local> m = magic_area_rect(&x, &y, &width, &height, area_);
map_foreachinarea(std::bind(find_entities_in_area_c, ph::_1, entities_vp, filter),
m,
x, y,
x + width, y + height,
BL::NUL /* filter elsewhere */);
}
- CASE (const AreaBar&, a_bar)
+ MATCH_CASE (const AreaBar&, a_bar)
{
(void)a_bar;
// TODO this is wrong
- map_local *m;
int x, y, width, height;
- magic_area_rect(&m, &x, &y, &width, &height, area_);
+ Borrowed<map_local> m = magic_area_rect(&x, &y, &width, &height, area_);
map_foreachinarea(std::bind(find_entities_in_area_c, ph::_1, entities_vp, filter),
m,
x, y,
@@ -1180,6 +1184,7 @@ void find_entities_in_area(area_t& area_,
BL::NUL /* filter elsewhere */);
}
}
+ MATCH_END ();
}
static
@@ -1312,13 +1317,13 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete)
dumb_ptr<effect_t> next = e->next;
int i;
- MATCH (*e)
+ MATCH_BEGIN (*e)
{
- CASE (const EffectSkip&, e_)
+ MATCH_CASE (const EffectSkip&, e_)
{
(void)e_;
}
- CASE (const EffectAbort&, e_)
+ MATCH_CASE (const EffectAbort&, e_)
{
(void)e_;
invocation_->flags |= INVOCATION_FLAG::ABORTED;
@@ -1326,34 +1331,34 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete)
clear_stack(invocation_);
next = nullptr;
}
- CASE (const EffectEnd&, e_)
+ MATCH_CASE (const EffectEnd&, e_)
{
(void)e_;
clear_stack(invocation_);
next = nullptr;
}
- CASE (const EffectAssign&, e_assign)
+ MATCH_CASE (const EffectAssign&, e_assign)
{
magic_eval(invocation_->env,
&invocation_->env->varu[e_assign.id],
e_assign.expr);
}
- CASE (const EffectForEach&, e_foreach)
+ MATCH_CASE (const EffectForEach&, e_foreach)
{
next = run_foreach(invocation_, &e_foreach, next);
}
- CASE (const EffectFor&, e_for)
+ MATCH_CASE (const EffectFor&, e_for)
{
next = run_for (invocation_, &e_for, next);
}
- CASE (const EffectIf&, e_if)
+ MATCH_CASE (const EffectIf&, e_if)
{
if (magic_eval_int(invocation_->env, e_if.cond))
next = e_if.true_branch;
else
next = e_if.false_branch;
}
- CASE (const EffectSleep&, e_)
+ MATCH_CASE (const EffectSleep&, e_)
{
interval_t sleeptime = static_cast<interval_t>(
magic_eval_int(invocation_->env, e_.e_sleep));
@@ -1361,7 +1366,7 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete)
if (sleeptime > interval_t::zero())
return sleeptime;
}
- CASE (const EffectScript&, e_)
+ MATCH_CASE (const EffectScript&, e_)
{
dumb_ptr<map_session_data> caster = map_id_is_player(invocation_->caster);
if (caster)
@@ -1391,7 +1396,7 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete)
// dealing with an NPC
int newpos = run_script_l(
- ScriptPointer(&*e_.e_script, invocation_->script_pos),
+ ScriptPointer(borrow(*e_.e_script), invocation_->script_pos),
message_recipient, invocation_->bl_id,
arg);
/* Returns the new script position, or -1 once the script is finished */
@@ -1408,12 +1413,12 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete)
}
REFRESH_INVOCATION; // Script may have killed the caster
}
- CASE (const EffectBreak&, e_)
+ MATCH_CASE (const EffectBreak&, e_)
{
(void)e_;
next = return_to_stack(invocation_);
}
- CASE (const EffectOp&, e_op)
+ MATCH_CASE (const EffectOp&, e_op)
{
op_t *op = e_op.opp;
val_t args[MAX_ARGS];
@@ -1432,11 +1437,12 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete)
REFRESH_INVOCATION; // Effect may have killed the caster
}
- CASE (const EffectCall&, e_call)
+ MATCH_CASE (const EffectCall&, e_call)
{
next = run_call(invocation_, &e_call, next);
}
}
+ MATCH_END ();
break_match:
if (!next)
@@ -1535,4 +1541,5 @@ int spell_attack(BlockId caster_id, BlockId target_id)
return 1;
}
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic-stmt.hpp b/src/map/magic-stmt.hpp
index 0385858..3b04fe3 100644
--- a/src/map/magic-stmt.hpp
+++ b/src/map/magic-stmt.hpp
@@ -21,17 +21,15 @@
#include "fwd.hpp"
-#include "../range/fwd.hpp"
-
#include "../strings/zstring.hpp"
-#include "../generic/fwd.hpp"
-
-#include "skill.t.hpp"
+#include "../mmo/skill.t.hpp"
namespace tmwa
{
+namespace map
+{
namespace magic
{
struct op_t
@@ -91,4 +89,5 @@ int spell_attack(BlockId caster, BlockId target);
void spell_free_invocation(dumb_ptr<invocation> invocation);
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic-stmt.py b/src/map/magic-stmt.py
index 14289ef..7cc43d0 100644
--- a/src/map/magic-stmt.py
+++ b/src/map/magic-stmt.py
@@ -1,7 +1,7 @@
class op_t(object):
__slots__ = ('_value')
- name = 'tmwa::magic::op_t'
+ name = 'tmwa::map::magic::op_t'
depth = 1
enabled = True
@@ -30,8 +30,8 @@ class op_t(object):
'''
tests = [
- ('static_cast<tmwa::magic::op_t *>(nullptr)',
+ ('static_cast<tmwa::map::magic::op_t *>(nullptr)',
'(op_t *) nullptr'),
- ('new tmwa::magic::op_t{"name"_s, "sig"_s, nullptr}',
- 'regex:\(op_t \*\) = \{->name = "name", ->signature = "sig", ->op = (0x)?0}'),
+ ('new tmwa::map::magic::op_t{"name"_s, "sig"_s, nullptr}',
+ '(op_t *) = {->name = "name", ->signature = "sig", ->op = nullptr}'),
]
diff --git a/src/map/magic-v2.cpp b/src/map/magic-v2.cpp
index 5b375b2..52b1b8f 100644
--- a/src/map/magic-v2.cpp
+++ b/src/map/magic-v2.cpp
@@ -24,6 +24,8 @@
#include <map>
#include <set>
+#include "../range/slice.hpp"
+
#include "../strings/rstring.hpp"
#include "../strings/literal.hpp"
@@ -34,27 +36,28 @@
#include "../sexpr/parser.hpp"
+#include "../ast/script.hpp"
+
+#include "globals.hpp"
#include "itemdb.hpp"
#include "magic-expr.hpp"
#include "magic-interpreter.hpp"
#include "magic-interpreter-base.hpp"
#include "magic-stmt.hpp"
+#include "script-parse.hpp"
#include "../poison.hpp"
namespace tmwa
{
+namespace map
+{
namespace magic
{
namespace magic_v2
{
static
- std::map<RString, proc_t> procs;
- static
- std::map<RString, val_t> const_defm;
-
- static
size_t intern_id(ZString id_name)
{
// TODO use InternPool
@@ -134,14 +137,15 @@ namespace magic_v2
/* For FOR and FOREACH, we use special stack handlers and thus don't have to set
* the continuation. It's only IF that we need to handle in this fashion. */
- MATCH (*src)
+ MATCH_BEGIN (*src)
{
- CASE (EffectIf&, e_if)
+ MATCH_CASE (EffectIf&, e_if)
{
set_effect_continuation(e_if.true_branch, continuation);
set_effect_continuation(e_if.false_branch, continuation);
}
}
+ MATCH_END ();
if (src->next)
set_effect_continuation(src->next, continuation);
@@ -172,13 +176,14 @@ namespace magic_v2
}
/* If the premise is a disjunction, b is the continuation of _all_ branches */
- MATCH (*a)
+ MATCH_BEGIN (*a)
{
- CASE(const GuardChoice&, s)
+ MATCH_CASE (const GuardChoice&, s)
{
spellguard_implication(s.s_alt, b);
}
}
+ MATCH_END ();
if (a->next)
spellguard_implication(a->next, b);
@@ -575,9 +580,9 @@ namespace magic_v2
{
count = 1;
- item_data *item = itemdb_searchname(s._str);
- if (!item)
- return fail(s, "no such item"_s);
+ Borrowed<item_data> item = TRY_UNWRAP(itemdb_searchname(s._str),
+ return fail(s, "no such item"_s)
+ );
id = item->nameid;
return true;
}
@@ -591,9 +596,9 @@ namespace magic_v2
if (s._list[1]._type != sexpr::STRING)
return fail(s._list[1], "item pair second not name"_s);
- item_data *item = itemdb_searchname(s._list[1]._str);
- if (!item)
- return fail(s, "no such item"_s);
+ Borrowed<item_data> item = TRY_UNWRAP(itemdb_searchname(s._list[1]._str),
+ return fail(s, "no such item"_s)
+ );
id = item->nameid;
return true;
}
@@ -782,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(STRPRINTF("script magic %s:%d"_fmt, begin.filename, begin.line), code, true);
if (!script)
return fail(s._list[1], "script does not compile"_s);
EffectScript e;
@@ -1273,4 +1291,5 @@ bool load_magic_file_v2(ZString filename)
return rv;
}
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic-v2.hpp b/src/map/magic-v2.hpp
index 9ad44a9..fac2773 100644
--- a/src/map/magic-v2.hpp
+++ b/src/map/magic-v2.hpp
@@ -25,10 +25,13 @@
namespace tmwa
{
+namespace map
+{
namespace magic
{
bool magic_init0();
// must be called after itemdb initialization
bool load_magic_file_v2(ZString filename);
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic.cpp b/src/map/magic.cpp
index a0238b5..418312a 100644
--- a/src/map/magic.cpp
+++ b/src/map/magic.cpp
@@ -28,6 +28,7 @@
#include "../io/cxxstdio.hpp"
+#include "globals.hpp"
#include "magic-expr.hpp"
#include "magic-interpreter.hpp"
#include "magic-interpreter-base.hpp"
@@ -40,6 +41,8 @@
namespace tmwa
{
+namespace map
+{
namespace magic
{
/// Return a pair of strings, {spellname, parameter}
@@ -71,7 +74,7 @@ int magic_message(dumb_ptr<map_session_data> caster, XString source_invocation)
{
if (pc_isdead(caster))
return 0;
- if (bool(caster->status.option & Option::HIDE))
+ if (bool(caster->status.option & Opt0::HIDE))
return 0; // No spellcasting while hidden
int power = caster->matk1;
@@ -123,4 +126,5 @@ int magic_message(dumb_ptr<map_session_data> caster, XString source_invocation)
return 0; /* Not a spell */
}
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/magic.hpp b/src/map/magic.hpp
index a420872..70d40dc 100644
--- a/src/map/magic.hpp
+++ b/src/map/magic.hpp
@@ -21,16 +21,14 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
-#include "../generic/fwd.hpp"
-
#include "map.t.hpp"
-#include "skill.t.hpp"
+#include "../mmo/skill.t.hpp"
namespace tmwa
{
+namespace map
+{
namespace magic
{
/**
@@ -46,4 +44,5 @@ namespace magic
*/
int magic_message(dumb_ptr<map_session_data> caster, XString source_invocation);
} // namespace magic
+} // namespace map
} // namespace tmwa
diff --git a/src/map/main.cpp b/src/map/main.cpp
index c16f642..f19272d 100644
--- a/src/map/main.cpp
+++ b/src/map/main.cpp
@@ -17,7 +17,7 @@
// 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 "../mmo/core.hpp"
+#include "../high/core.hpp"
#include "map.hpp"
@@ -26,6 +26,9 @@
namespace tmwa
{
+namespace map
+{
+} // namespace map
} // namespace tmwa
int main(int argc, char **argv)
diff --git a/src/map/map.cpp b/src/map/map.cpp
index 4a25029..c1d760a 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -45,34 +45,41 @@
#include "../generic/random2.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
+#include "../io/extract.hpp"
#include "../io/read.hpp"
+#include "../io/span.hpp"
#include "../io/tty.hpp"
#include "../io/write.hpp"
#include "../net/socket.hpp"
#include "../net/timer.hpp"
+#include "../net/timestamp-utils.hpp"
#include "../mmo/config_parse.hpp"
-#include "../mmo/core.hpp"
-#include "../mmo/extract.hpp"
-#include "../mmo/utils.hpp"
+#include "../mmo/cxxstdio_enums.hpp"
#include "../mmo/version.hpp"
+#include "../high/core.hpp"
+
#include "atcommand.hpp"
#include "battle.hpp"
+#include "battle_conf.hpp"
#include "chrif.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "grfio.hpp"
#include "itemdb.hpp"
#include "magic-interpreter.hpp" // for is_spell inline body
#include "magic-stmt.hpp"
#include "magic-v2.hpp"
+#include "map_conf.hpp"
#include "mob.hpp"
+#include "quest.hpp"
#include "npc.hpp"
+#include "npc-parse.hpp"
#include "party.hpp"
#include "pc.hpp"
-#include "script.hpp"
+#include "script-startup.hpp"
#include "skill.hpp"
#include "storage.hpp"
#include "trade.hpp"
@@ -82,53 +89,16 @@
namespace tmwa
{
-DMap<BlockId, dumb_ptr<block_list>> id_db;
-
-UPMap<MapName, map_abstract> maps_db;
-
-static
-DMap<CharName, dumb_ptr<map_session_data>> nick_db;
-
-struct charid2nick
-{
- CharName nick;
- int req_id;
-};
-
-static
-Map<CharId, struct charid2nick> charid_db;
-
-static
-int users = 0;
-static
-Array<dumb_ptr<block_list>, unwrap<BlockId>(MAX_FLOORITEM)> object;
-static
-BlockId first_free_object_id = BlockId();
-
-interval_t autosave_time = DEFAULT_AUTOSAVE_INTERVAL;
-int save_settings = 0xFFFF;
-
-AString motd_txt = "conf/motd.txt"_s;
-
-CharName wisp_server_name = stringish<CharName>("Server"_s); // can be modified in char-server configuration file
-
-static
-void map_delmap(MapName mapname);
-
void SessionDeleter::operator()(SessionData *sd)
{
- really_delete1 static_cast<map_session_data *>(sd);
+ really_delete1 static_cast<map::map_session_data *>(sd);
}
-VString<49> convert_for_printf(NpcEvent ev)
+namespace map
{
- return STRNPRINTF(50, "%s::%s"_fmt, ev.npc, ev.label);
-}
-bool extract(XString str, NpcEvent *ev)
-{
- XString mid;
- return extract(str, record<':'>(&ev->npc, &mid, &ev->label)) && !mid;
-}
+const CharName WISP_SERVER_NAME = stringish<CharName>("Server"_s);
+
+map_local undefined_gat = [](){ map_local rv {}; rv.name_ = stringish<MapName>("undefined.gat"_s); return rv; }();
/*==========================================
* 全map鯖総計での接続数設定
@@ -137,7 +107,7 @@ bool extract(XString str, NpcEvent *ev)
*/
void map_setusers(int n)
{
- users = n;
+ world_user_count = n;
}
/*==========================================
@@ -146,14 +116,9 @@ void map_setusers(int n)
*/
int map_getusers(void)
{
- return users;
+ return world_user_count;
}
-static
-int block_free_lock = 0;
-static
-std::vector<dumb_ptr<block_list>> block_free;
-
void MapBlockLock::freeblock(dumb_ptr<block_list> bl)
{
if (block_free_lock == 0)
@@ -178,12 +143,6 @@ MapBlockLock::~MapBlockLock()
}
}
-/// This is a dummy entry that is shared by all the linked lists,
-/// so that any entry can unlink itself without worrying about
-/// whether it was the the head of the list.
-static
-struct block_list bl_head;
-
/*==========================================
* map[]のblock_listに追加
* mobは数が多いので別リスト
@@ -202,10 +161,10 @@ int map_addblock(dumb_ptr<block_list> bl)
return 0;
}
- map_local *m = bl->bl_m;
+ P<map_local> m = bl->bl_m;
int x = bl->bl_x;
int y = bl->bl_y;
- if (!m ||
+ if (m == borrow(undefined_gat) ||
x < 0 || x >= m->xs || y < 0 || y >= m->ys)
return 1;
@@ -283,7 +242,7 @@ int map_delblock(dumb_ptr<block_list> bl)
* セル上のPCとMOBの数を数える (グランドクロス用)
*------------------------------------------
*/
-int map_count_oncell(map_local *m, int x, int y)
+int map_count_oncell(Borrowed<map_local> m, int x, int y)
{
int bx, by;
dumb_ptr<block_list> bl = nullptr;
@@ -318,14 +277,17 @@ int map_count_oncell(map_local *m, int x, int y)
*------------------------------------------
*/
void map_foreachinarea(std::function<void(dumb_ptr<block_list>)> func,
- map_local *m,
+ Borrowed<map_local> m,
int x0, int y0, int x1, int y1,
BL type)
{
std::vector<dumb_ptr<block_list>> bl_list;
- if (!m)
- return;
+ // there are some broadcasts during startup
+ // disable then
+ if (m == borrow(undefined_gat))
+ abort();
+
if (x0 < 0)
x0 = 0;
if (y0 < 0)
@@ -381,7 +343,7 @@ void map_foreachinarea(std::function<void(dumb_ptr<block_list>)> func,
*------------------------------------------
*/
void map_foreachinmovearea(std::function<void(dumb_ptr<block_list>)> func,
- map_local *m,
+ Borrowed<map_local> m,
int x0, int y0, int x1, int y1,
int dx, int dy,
BL type)
@@ -501,7 +463,7 @@ void map_foreachinmovearea(std::function<void(dumb_ptr<block_list>)> func,
// area radius - may be more useful in some instances)
//
void map_foreachincell(std::function<void(dumb_ptr<block_list>)> func,
- map_local *m,
+ Borrowed<map_local> m,
int x, int y,
BL type)
{
@@ -674,7 +636,7 @@ void map_clearflooritem_timer(TimerData *tid, tick_t, BlockId id)
map_delobject(fitem->bl_id, BL::ITEM);
}
-std::pair<uint16_t, uint16_t> map_randfreecell(map_local *m,
+std::pair<uint16_t, uint16_t> map_randfreecell(Borrowed<map_local> m,
uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
for (int itr : random_::iterator(w * h))
@@ -689,7 +651,7 @@ std::pair<uint16_t, uint16_t> map_randfreecell(map_local *m,
/// Return a randomly selected passable cell within a given range.
static
-std::pair<uint16_t, uint16_t> map_searchrandfreecell(map_local *m, int x, int y, int range)
+std::pair<uint16_t, uint16_t> map_searchrandfreecell(Borrowed<map_local> m, int x, int y, int range)
{
int whole_range = 2 * range + 1;
return map_randfreecell(m, x - range, y - range, whole_range, whole_range);
@@ -702,7 +664,7 @@ std::pair<uint16_t, uint16_t> map_searchrandfreecell(map_local *m, int x, int y,
*------------------------------------------
*/
BlockId map_addflooritem_any(Item *item_data, int amount,
- map_local *m, int x, int y,
+ Borrowed<map_local> m, int x, int y,
dumb_ptr<map_session_data> *owners, interval_t *owner_protection,
interval_t lifetime, int dispersal)
{
@@ -767,7 +729,7 @@ BlockId map_addflooritem_any(Item *item_data, int amount,
}
BlockId map_addflooritem(Item *item_data, int amount,
- map_local *m, int x, int y,
+ Borrowed<map_local> m, int x, int y,
dumb_ptr<map_session_data> first_sd,
dumb_ptr<map_session_data> second_sd,
dumb_ptr<map_session_data> third_sd)
@@ -776,14 +738,14 @@ BlockId map_addflooritem(Item *item_data, int amount,
interval_t owner_protection[3];
{
- owner_protection[0] = static_cast<interval_t>(battle_config.item_first_get_time);
- owner_protection[1] = owner_protection[0] + static_cast<interval_t>(battle_config.item_second_get_time);
- owner_protection[2] = owner_protection[1] + static_cast<interval_t>(battle_config.item_third_get_time);
+ owner_protection[0] = battle_config.item_first_get_time;
+ owner_protection[1] = owner_protection[0] + battle_config.item_second_get_time;
+ owner_protection[2] = owner_protection[1] + battle_config.item_third_get_time;
}
return map_addflooritem_any(item_data, amount, m, x, y,
owners, owner_protection,
- static_cast<interval_t>(battle_config.flooritem_lifetime), 1);
+ battle_config.flooritem_lifetime, 1);
}
/*==========================================
@@ -792,9 +754,7 @@ BlockId map_addflooritem(Item *item_data, int amount,
*/
void map_addchariddb(CharId charid, CharName name)
{
- struct charid2nick *p = charid_db.search(charid);
- if (p == nullptr)
- p = charid_db.init(charid);
+ P<struct charid2nick> p = charid_db.init(charid);
p->nick = name;
p->req_id = 0;
@@ -929,13 +889,17 @@ dumb_ptr<map_session_data> map_id2sd(BlockId id)
*/
CharName map_charid2nick(CharId id)
{
- struct charid2nick *p = charid_db.search(id);
+ Option<P<struct charid2nick>> p_ = charid_db.search(id);
- if (p == nullptr)
- return CharName();
- if (p->req_id != 0)
- return CharName();
- return p->nick;
+ return p_.cmap(
+ [](P<struct charid2nick> p)
+ {
+ return p->req_id == 0;
+ },
+ [](P<struct charid2nick> p)
+ {
+ return p->nick;
+ }).move_or(CharName());
}
/*========================================*/
@@ -1047,11 +1011,9 @@ dumb_ptr<block_list> map_id2bl(BlockId id)
* map.npcへ追加 (warp等の領域持ちのみ)
*------------------------------------------
*/
-int map_addnpc(map_local *m, dumb_ptr<npc_data> nd)
+int map_addnpc(Borrowed<map_local> m, dumb_ptr<npc_data> nd)
{
int i;
- if (!m)
- return -1;
for (i = 0; i < m->npc_num && i < MAX_NPC_PER_MAP; i++)
if (m->npc[i] == nullptr)
break;
@@ -1109,27 +1071,39 @@ void map_removenpc(void)
* map名からmap番号へ変換
*------------------------------------------
*/
-map_local *map_mapname2mapid(MapName name)
+Option<Borrowed<map_local>> map_mapname2mapid(MapName name)
{
- map_abstract *md = maps_db.get(name);
- if (md == nullptr || md->gat == nullptr)
- return nullptr;
- return static_cast<map_local *>(md);
+ Option<P<map_abstract>> md_ = maps_db.get(name);
+ return md_.cmap(
+ [](P<map_abstract> md)
+ {
+ return bool(md->gat);
+ },
+ [](P<map_abstract> md)
+ {
+ return md.downcast_to<map_local>();
+ });
}
/*==========================================
* 他鯖map名からip,port変換
*------------------------------------------
*/
-int map_mapname2ipport(MapName name, IP4Address *ip, int *port)
+int map_mapname2ipport(MapName name, Borrowed<IP4Address> ip, Borrowed<int> port)
{
- map_abstract *md = maps_db.get(name);
- if (md == nullptr || md->gat)
- return -1;
- map_remote *mdos = static_cast<map_remote *>(md);
- *ip = mdos->ip;
- *port = mdos->port;
- return 0;
+ Option<P<map_abstract>> md_ = maps_db.get(name);
+ return md_.cmap(
+ [](P<map_abstract> md)
+ {
+ return !md->gat;
+ },
+ [ip, port](P<map_abstract> md)
+ {
+ auto mdos = md.downcast_to<map_remote>();
+ *ip = mdos->ip;
+ *port = mdos->port;
+ return 0;
+ }).copy_or(-1);
}
/// Check compatibility of directions.
@@ -1208,7 +1182,7 @@ DIR map_calc_dir(dumb_ptr<block_list> src, int x, int y)
* (m,x,y)の状態を調べる
*------------------------------------------
*/
-MapCell map_getcell(map_local *m, int x, int y)
+MapCell map_getcell(Borrowed<map_local> m, int x, int y)
{
if (x < 0 || x >= m->xs - 1 || y < 0 || y >= m->ys - 1)
return MapCell::UNWALKABLE;
@@ -1219,7 +1193,7 @@ MapCell map_getcell(map_local *m, int x, int y)
* (m,x,y)の状態をtにする
*------------------------------------------
*/
-void map_setcell(map_local *m, int x, int y, MapCell t)
+void map_setcell(Borrowed<map_local> m, int x, int y, MapCell t)
{
if (x < 0 || x >= m->xs || y < 0 || y >= m->ys)
return;
@@ -1232,37 +1206,41 @@ void map_setcell(map_local *m, int x, int y, MapCell t)
*/
int map_setipport(MapName name, IP4Address ip, int port)
{
- map_abstract *md = maps_db.get(name);
- if (md == nullptr)
+ Option<P<map_abstract>> md_ = maps_db.get(name);
+ OMATCH_BEGIN (md_)
{
- // not exist -> add new data
- auto mdos = make_unique<map_remote>();
- mdos->name_ = name;
- mdos->gat = nullptr;
- mdos->ip = ip;
- mdos->port = port;
- maps_db.put(mdos->name_, std::move(mdos));
- }
- else
- {
- if (md->gat)
+ OMATCH_CASE_SOME (md)
{
- // local -> check data
- if (ip != clif_getip() || port != clif_getport())
+ if (md->gat)
+ {
+ // local -> check data
+ if (ip != map_conf.map_ip || port != map_conf.map_port)
+ {
+ PRINTF("from char server : %s -> %s:%d\n"_fmt,
+ name, ip, port);
+ return 1;
+ }
+ }
+ else
{
- PRINTF("from char server : %s -> %s:%d\n"_fmt,
- name, ip, port);
- return 1;
+ // update
+ P<map_remote> mdos = md.downcast_to<map_remote>();
+ mdos->ip = ip;
+ mdos->port = port;
}
}
- else
+ OMATCH_CASE_NONE ()
{
- // update
- map_remote *mdos = static_cast<map_remote *>(md);
+ // not exist -> add new data
+ auto mdos = make_unique<map_remote>();
+ mdos->name_ = name;
+ mdos->gat = nullptr;
mdos->ip = ip;
mdos->port = port;
+ maps_db.put(mdos->name_, std::move(mdos));
}
}
+ OMATCH_END ();
return 0;
}
@@ -1281,7 +1259,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);
@@ -1334,7 +1312,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,
@@ -1349,7 +1327,6 @@ bool map_readallmap(void)
* 読み込むmapを追加する
*------------------------------------------
*/
-static
void map_addmap(MapName mapname)
{
if (mapname == "clear"_s)
@@ -1384,18 +1361,11 @@ void map_delmap(MapName mapname)
constexpr int LOGFILE_SECONDS_PER_CHUNK_SHIFT = 10;
static
-std::unique_ptr<io::AppendFile> map_logfile;
-static
-AString map_logfile_name;
-static
-long map_logfile_index;
-
-static
void map_close_logfile(void)
{
if (map_logfile)
{
- AString filename = STRPRINTF("%s.%ld"_fmt, map_logfile_name, map_logfile_index);
+ AString filename = STRPRINTF("%s.%ld"_fmt, map_conf.log_file, map_logfile_index);
const char *args[] =
{
"gzip",
@@ -1423,22 +1393,23 @@ void map_start_logfile(long index)
AString filename_buf = STRPRINTF(
"%s.%ld"_fmt,
- map_logfile_name,
+ map_conf.log_file,
map_logfile_index);
map_logfile = make_unique<io::AppendFile>(filename_buf);
if (!map_logfile->is_open())
{
map_logfile.reset();
- perror(map_logfile_name.c_str());
+ perror(map_conf.log_file.c_str());
}
}
static
-void map_set_logfile(AString filename)
+void map_set_logfile()
{
- struct timeval tv;
+ if (!map_conf.log_file)
+ return;
- map_logfile_name = std::move(filename);
+ struct timeval tv;
gettimeofday(&tv, nullptr);
map_start_logfile(tv.tv_sec >> LOGFILE_SECONDS_PER_CHUNK_SHIFT);
@@ -1463,147 +1434,6 @@ void map_log(XString line)
log_with_timestamp(*map_logfile, line);
}
-/*==========================================
- * 設定ファイルを読み込む
- *------------------------------------------
- */
-static
-bool map_config_read(ZString cfgName)
-{
- struct hostent *h = nullptr;
-
- io::ReadFile in(cfgName);
- if (!in.is_open())
- {
- PRINTF("Map configuration file not found at: %s\n"_fmt, cfgName);
- return false;
- }
-
- bool rv = true;
- AString line;
- while (in.getline(line))
- {
- if (is_comment(line))
- continue;
- XString w1;
- ZString w2;
- if (!config_split(line, &w1, &w2))
- {
- PRINTF("Bad config line: %s\n"_fmt, line);
- rv = false;
- continue;
- }
- if (w1 == "userid"_s)
- {
- AccountName name = stringish<AccountName>(w2);
- chrif_setuserid(name);
- }
- else if (w1 == "passwd"_s)
- {
- AccountPass pass = stringish<AccountPass>(w2);
- chrif_setpasswd(pass);
- }
- else if (w1 == "char_ip"_s)
- {
- h = gethostbyname(w2.c_str());
- IP4Address w2ip;
- if (h != nullptr)
- {
- w2ip = IP4Address({
- static_cast<uint8_t>(h->h_addr[0]),
- static_cast<uint8_t>(h->h_addr[1]),
- static_cast<uint8_t>(h->h_addr[2]),
- static_cast<uint8_t>(h->h_addr[3]),
- });
- PRINTF("Character server IP address : %s -> %s\n"_fmt,
- w2, w2ip);
- }
- else
- {
- PRINTF("Bad IP value: %s\n"_fmt, line);
- return false;
- }
- chrif_setip(w2ip);
- }
- else if (w1 == "char_port"_s)
- {
- chrif_setport(atoi(w2.c_str()));
- }
- else if (w1 == "map_ip"_s)
- {
- h = gethostbyname(w2.c_str());
- IP4Address w2ip;
- if (h != nullptr)
- {
- w2ip = IP4Address({
- static_cast<uint8_t>(h->h_addr[0]),
- static_cast<uint8_t>(h->h_addr[1]),
- static_cast<uint8_t>(h->h_addr[2]),
- static_cast<uint8_t>(h->h_addr[3]),
- });
- PRINTF("Map server IP address : %s -> %s\n"_fmt,
- w2, w2ip);
- }
- else
- {
- PRINTF("Bad IP value: %s\n"_fmt, line);
- return false;
- }
- clif_setip(w2ip);
- }
- else if (w1 == "map_port"_s)
- {
- clif_setport(atoi(w2.c_str()));
- }
- else if (w1 == "map"_s)
- {
- MapName name = VString<15>(w2);
- map_addmap(name);
- }
- else if (w1 == "delmap"_s)
- {
- MapName name = VString<15>(w2);
- map_delmap(name);
- }
- else if (w1 == "npc"_s)
- {
- npc_addsrcfile(w2);
- }
- else if (w1 == "delnpc"_s)
- {
- npc_delsrcfile(w2);
- }
- else if (w1 == "autosave_time"_s)
- {
- autosave_time = std::chrono::seconds(atoi(w2.c_str()));
- if (autosave_time <= interval_t::zero())
- autosave_time = DEFAULT_AUTOSAVE_INTERVAL;
- }
- else if (w1 == "motd_txt"_s)
- {
- motd_txt = w2;
- }
- else if (w1 == "mapreg_txt"_s)
- {
- mapreg_txt = w2;
- }
- else if (w1 == "gm_log"_s)
- {
- gm_log = std::move(w2);
- }
- else if (w1 == "log_file"_s)
- {
- map_set_logfile(w2);
- }
- else if (w1 == "import"_s)
- {
- rv &= map_config_read(w2);
- }
- }
-
- return rv;
-}
-
static
void cleanup_sub(dumb_ptr<block_list> bl)
{
@@ -1629,17 +1459,86 @@ void cleanup_sub(dumb_ptr<block_list> bl)
}
}
+int compare_item(Item *a, Item *b)
+{
+ return (a->nameid == b->nameid);
+}
+
+static
+bool map_config(io::Spanned<XString> key, io::Spanned<ZString> value)
+{
+ return parse_map_conf(map_conf, key, value);
+}
+
+static
+bool battle_config_(io::Spanned<XString> key, io::Spanned<ZString> value)
+{
+ return parse_battle_conf(battle_config, key, value);
+}
+
+static
+bool map_confs(io::Spanned<XString> key, io::Spanned<ZString> value)
+{
+ if (key.data == "map_conf"_s)
+ return load_config_file(value.data, map_config);
+ if (key.data == "battle_conf"_s)
+ return load_config_file(value.data, battle_config_);
+ if (key.data == "atcommand_conf"_s)
+ return atcommand_config_read(value.data);
+
+ if (key.data == "item_db"_s)
+ return itemdb_readdb(value.data);
+ if (key.data == "mob_db"_s)
+ return mob_readdb(value.data);
+ if (key.data == "quest_db"_s)
+ return quest_readdb(value.data);
+ if (key.data == "mob_skill_db"_s)
+ return mob_readskilldb(value.data);
+ if (key.data == "skill_db"_s)
+ return skill_readdb(value.data);
+ if (key.data == "magic_conf"_s)
+ return magic::load_magic_file_v2(value.data);
+
+ if (key.data == "resnametable"_s)
+ return load_resnametable(value.data);
+ if (key.data == "const_db"_s)
+ return read_constdb(value.data);
+ key.span.error("Unknown meta-key for map server"_s);
+ return false;
+}
+
+int map_scriptcont(dumb_ptr<map_session_data> sd, BlockId id)
+{
+ dumb_ptr<block_list> bl = map_id2bl(id);
+
+ if (!bl)
+ return 0;
+
+ switch (bl->bl_type)
+ {
+ case BL::NPC:
+ return npc_scriptcont(sd, id);
+ case BL::SPELL:
+ magic::spell_execute_script(bl->is_spell());
+ break;
+ }
+
+ return 0;
+}
+} // namespace map
+
/*==========================================
* map鯖終了時処理
*------------------------------------------
*/
void term_func(void)
{
+ using namespace tmwa::map;
for (auto& mit : maps_db)
{
if (!mit.second->gat)
continue;
- map_local *map_id = static_cast<map_local *>(mit.second.get());
+ P<map_local> map_id = borrow(*mit.second).downcast_to<map_local>();
map_foreachinarea(cleanup_sub,
map_id,
@@ -1662,46 +1561,14 @@ void term_func(void)
map_close_logfile();
}
-int compare_item(Item *a, Item *b)
-{
- return (a->nameid == b->nameid);
-}
-
-static
-bool map_confs(XString key, ZString value)
-{
- if (key == "map_conf"_s)
- return map_config_read(value);
- if (key == "battle_conf"_s)
- return battle_config_read(value);
- if (key == "atcommand_conf"_s)
- return atcommand_config_read(value);
-
- if (key == "item_db"_s)
- return itemdb_readdb(value);
- if (key == "mob_db"_s)
- return mob_readdb(value);
- if (key == "mob_skill_db"_s)
- return mob_readskilldb(value);
- if (key == "skill_db"_s)
- return skill_readdb(value);
- if (key == "magic_conf"_s)
- return magic::load_magic_file_v2(value);
-
- if (key == "resnametable"_s)
- return load_resnametable(value);
- if (key == "const_db"_s)
- return read_constdb(value);
- PRINTF("unknown map conf key: %s\n"_fmt, AString(key));
- return false;
-}
-
/*======================================================
* Map-Server Init and Command-line Arguments [Valaris]
*------------------------------------------------------
*/
int do_init(Slice<ZString> argv)
{
+ using namespace tmwa::map;
+
ZString argv0 = argv.pop_front();
runflag &= magic::magic_init0();
@@ -1749,7 +1616,8 @@ int do_init(Slice<ZString> argv)
if (!loaded_config_yet)
runflag &= load_config_file("conf/tmwa-map.conf"_s, map_confs);
- battle_config_check();
+ map_set_logfile();
+
runflag &= map_readallmap();
do_init_chrif();
@@ -1767,26 +1635,7 @@ int do_init(Slice<ZString> argv)
PRINTF("The server is running in " SGR_BOLD SGR_RED "PK Mode" SGR_RESET "\n"_fmt);
PRINTF("The map-server is " SGR_BOLD SGR_GREEN "ready" SGR_RESET " (Server is listening on the port %d).\n\n"_fmt,
- clif_getport());
-
- return 0;
-}
-
-int map_scriptcont(dumb_ptr<map_session_data> sd, BlockId id)
-{
- dumb_ptr<block_list> bl = map_id2bl(id);
-
- if (!bl)
- return 0;
-
- switch (bl->bl_type)
- {
- case BL::NPC:
- return npc_scriptcont(sd, id);
- case BL::SPELL:
- magic::spell_execute_script(bl->is_spell());
- break;
- }
+ map_conf.map_port);
return 0;
}
diff --git a/src/map/map.hpp b/src/map/map.hpp
index d88ff54..f57dcee 100644
--- a/src/map/map.hpp
+++ b/src/map/map.hpp
@@ -20,17 +20,16 @@
// 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 "fwd.hpp"
-
#include "map.t.hpp"
+#include "fwd.hpp"
+
#include <chrono>
#include <functional>
#include <list>
#include "../ints/udl.hpp"
-#include "../strings/fwd.hpp"
#include "../strings/rstring.hpp"
#include "../strings/astring.hpp"
#include "../strings/vstring.hpp"
@@ -42,18 +41,19 @@
#include "../net/socket.hpp"
#include "../net/timer.t.hpp"
-#include "../mmo/utils.hpp"
-
#include "battle.t.hpp"
-#include "clif.t.hpp"
+#include "../mmo/clif.t.hpp"
#include "mapflag.hpp"
#include "mob.t.hpp"
-#include "script.hpp" // change to script.t.hpp
-#include "skill.t.hpp"
+#include "script-buffer.hpp"
+#include "script-persist.hpp"
+#include "../mmo/skill.t.hpp"
namespace tmwa
{
+namespace map
+{
constexpr int MAX_NPC_PER_MAP = 512;
constexpr int BLOCK_SIZE = 8;
#define AREA_SIZE battle_config.area_size
@@ -61,47 +61,19 @@ constexpr std::chrono::seconds LIFETIME_FLOORITEM = 1_min;
constexpr int MAX_SKILL_LEVEL = 100;
constexpr int MAX_EVENTTIMER = 32;
constexpr interval_t NATURAL_HEAL_INTERVAL = 500_ms;
-constexpr BlockId MAX_FLOORITEM = wrap<BlockId>(500000_u32);
constexpr int MAX_LEVEL = 255;
constexpr int MAX_WALKPATH = 48;
constexpr int MAX_DROP_PER_MAP = 48;
-constexpr interval_t DEFAULT_AUTOSAVE_INTERVAL = 1_min;
-
-// formerly VString<49>, as name::label
-struct NpcEvent
-{
- NpcName npc;
- ScriptLabel label;
+constexpr std::chrono::seconds DEFAULT_AUTOSAVE_INTERVAL = 1_min;
- explicit operator bool()
- {
- return npc || label;
- }
- bool operator !()
- {
- return !bool(*this);
- }
-
- friend bool operator == (const NpcEvent& l, const NpcEvent& r)
- {
- return l.npc == r.npc && l.label == r.label;
- }
-
- friend bool operator < (const NpcEvent& l, const NpcEvent& r)
- {
- return l.npc < r.npc || (l.npc == r.npc && l.label < r.label);
- }
-
- friend VString<49> convert_for_printf(NpcEvent ev);
-};
-bool extract(XString str, NpcEvent *ev);
+extern map_local undefined_gat;
struct block_list
{
dumb_ptr<block_list> bl_next, bl_prev;
BlockId bl_id;
- map_local *bl_m;
+ Borrowed<map_local> bl_m = borrow(undefined_gat);
short bl_x, bl_y;
BL bl_type;
@@ -156,7 +128,7 @@ struct map_session_data : block_list, SessionData
unsigned dead_sit:2;
unsigned skillcastcancel:1;
unsigned waitingdisconnect:1;
- unsigned lr_flag:2;
+ unsigned lr_flag_is_arrow_2:1;
unsigned connect_new:1;
unsigned arrow_atk:1;
BF attack_type;//:3;
@@ -170,11 +142,10 @@ struct map_session_data : block_list, SessionData
unsigned shroud_disappears_on_pickup:1;
unsigned shroud_disappears_on_talk:1;
unsigned seen_motd:1;
+ unsigned pvpchannel;
} state;
struct
{
- unsigned killer:1;
- unsigned killable:1;
unsigned unbreakable_weapon:1;
unsigned unbreakable_armor:1;
unsigned deaf:1;
@@ -185,7 +156,19 @@ struct map_session_data : block_list, SessionData
unsigned char tmw_version; // tmw client version
CharKey status_key;
CharData status;
- GenericArray<struct item_data *, InventoryIndexing<IOff0, MAX_INVENTORY>> inventory_data;
+ GenericArray<Option<Borrowed<struct item_data>>, InventoryIndexing<IOff0, MAX_INVENTORY>> inventory_data =
+ {{
+ None, None, None, None, None, None, None, None, None, None,
+ None, None, None, None, None, None, None, None, None, None,
+ None, None, None, None, None, None, None, None, None, None,
+ None, None, None, None, None, None, None, None, None, None,
+ None, None, None, None, None, None, None, None, None, None,
+ None, None, None, None, None, None, None, None, None, None,
+ None, None, None, None, None, None, None, None, None, None,
+ None, None, None, None, None, None, None, None, None, None,
+ None, None, None, None, None, None, None, None, None, None,
+ None, None, None, None, None, None, None, None, None, None,
+ }}; // explicit is better than implicit
earray<IOff0, EQUIP, EQUIP::COUNT> equip_index_maybe;
int weight, max_weight;
MapName mapname_;
@@ -205,13 +188,12 @@ struct map_session_data : block_list, SessionData
int npc_amount;
// I have no idea exactly what these are doing ...
// but one should probably be replaced with a ScriptPointer ???
- const ScriptBuffer *npc_script, *npc_scriptroot;
+ Option<Borrowed<const ScriptBuffer>> npc_script = None, npc_scriptroot = None;
std::vector<struct script_data> npc_stackbuf;
RString npc_str;
struct
{
unsigned storage:1;
- unsigned divorce:1;
} npc_flags;
Timer attacktimer;
@@ -235,7 +217,7 @@ struct map_session_data : block_list, SessionData
short spellpower_bonus_target, spellpower_bonus_current; // [Fate] Spellpower boni. _current is the active one.
//_current slowly approximates _target, and _target is determined by equipment.
- short attackrange, attackrange_;
+ short attackrange;
// [Fate] Used for gradual healing; amount of enqueued regeneration
struct quick_regeneration quick_regeneration_hp, quick_regeneration_sp;
@@ -249,18 +231,17 @@ struct map_session_data : block_list, SessionData
interval_t hp_sub, sp_sub;
interval_t inchealhptick, inchealsptick;
- ItemLook weapontype1, weapontype2;
+ ItemLook weapontype1;
earray<int, ATTR, ATTR::COUNT> paramb, paramc, parame, paramcard;
int hit, flee, flee2;
interval_t aspd, amotion, dmotion;
int watk, watk2;
int def, def2, mdef, mdef2, critical, matk1, matk2;
int hprate, sprate, dsprate;
- int watk_, watk_2;
int base_atk, atk_rate;
int arrow_atk;
int arrow_cri, arrow_hit, arrow_range;
- int nhealhp, nhealsp, nshealhp, nshealsp, nsshealhp, nsshealsp;
+ int nhealhp, nhealsp;
int aspd_rate, speed_rate, hprecov_rate, sprecov_rate, critical_def,
double_rate;
int matk_rate;
@@ -269,9 +250,6 @@ struct map_session_data : block_list, SessionData
mdef_rate, mdef2_rate;
int double_add_rate, speed_add_rate, aspd_add_rate, perfect_hit_add;
short hp_drain_rate, hp_drain_per, sp_drain_rate, sp_drain_per;
- short hp_drain_rate_, hp_drain_per_, sp_drain_rate_, sp_drain_per_;
- short break_weapon_rate, break_armor_rate;
- short add_steal_rate;
int die_counter;
@@ -283,7 +261,6 @@ struct map_session_data : block_list, SessionData
Map<SIR, RString> regstrm;
earray<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
- short sc_count;
AccountId trade_partner;
Array<IOff2, TRADE_MAX> deal_item_index;
@@ -298,11 +275,8 @@ struct map_session_data : block_list, SessionData
PartyId partyspy; // [Syrus22]
- int catch_target_class;
-
int pvp_point, pvp_rank;
Timer pvp_timer;
- int pvp_lastusers;
std::list<NpcEvent> eventqueuel;
Array<Timer, MAX_EVENTTIMER> eventtimer;
@@ -312,14 +286,14 @@ struct map_session_data : block_list, SessionData
unsigned in_progress:1;
} auto_ban_info;
- TimeT chat_reset_due;
- TimeT chat_repeat_reset_due;
+ tick_t chat_reset_due;
+ tick_t chat_repeat_reset_due;
int chat_lines_in;
int chat_total_repeats;
RString chat_lastmsg;
tick_t flood_rates[0x220];
- TimeT packet_flood_reset_due;
+ tick_t packet_flood_reset_due;
int packet_flood_in;
IP4Address get_ip()
@@ -355,7 +329,7 @@ struct npc_data : block_list
Opt1 opt1;
Opt2 opt2;
Opt3 opt3;
- Option option;
+ Opt0 option;
short flag;
std::list<RString> eventqueuel;
@@ -383,6 +357,7 @@ public:
std::unique_ptr<const ScriptBuffer> script;
// Diameter.
short xs, ys;
+ bool event_needs_map;
// Whether the timer advances if not beyond end.
bool timer_active;
@@ -439,7 +414,7 @@ struct mob_data : block_list
MobMode mode;
struct
{
- map_local *m;
+ Borrowed<map_local> m = borrow(undefined_gat);
short x0, y0, xs, ys;
interval_t delay1, delay2;
} spawn;
@@ -478,11 +453,10 @@ struct mob_data : block_list
std::vector<Item> lootitemv;
earray<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
- short sc_count;
Opt1 opt1;
Opt2 opt2;
Opt3 opt3;
- Option option;
+ Opt0 option;
short min_chase;
Timer deletetimer;
@@ -514,10 +488,10 @@ struct map_abstract
// gat is nullptr for map_remote and non-nullptr for map_local
std::unique_ptr<MapCell[]> gat;
+ map_abstract() = default;
+ map_abstract(map_abstract&&) = default;
virtual ~map_abstract() {}
};
-extern
-UPMap<MapName, map_abstract> maps_db;
struct map_local : map_abstract
{
@@ -538,7 +512,7 @@ struct map_remote : map_abstract
};
inline
-MapCell read_gatp(map_local *m, int x, int y)
+MapCell read_gatp(Borrowed<map_local> m, int x, int y)
{
assert (0 <= x && x < m->xs);
assert (0 <= y && y < m->ys);
@@ -554,12 +528,7 @@ struct flooritem_data : block_list
Item item_data;
};
-extern interval_t autosave_time;
-extern int save_settings;
-
-extern AString motd_txt;
-
-extern CharName wisp_server_name;
+extern const CharName WISP_SERVER_NAME;
// 鯖全体情報
void map_setusers(int);
@@ -580,21 +549,21 @@ public:
int map_addblock(dumb_ptr<block_list>);
int map_delblock(dumb_ptr<block_list>);
void map_foreachinarea(std::function<void(dumb_ptr<block_list>)>,
- map_local *,
+ Borrowed<map_local>,
int, int, int, int,
BL);
// -- moonsoul (added map_foreachincell)
void map_foreachincell(std::function<void(dumb_ptr<block_list>)>,
- map_local *,
+ Borrowed<map_local>,
int, int,
BL);
void map_foreachinmovearea(std::function<void(dumb_ptr<block_list>)>,
- map_local *,
+ Borrowed<map_local>,
int, int, int, int,
int, int,
BL);
//block関連に追加
-int map_count_oncell(map_local *m, int x, int y);
+int map_count_oncell(Borrowed<map_local> m, int x, int y);
// 一時的object関連
BlockId map_addobject(dumb_ptr<block_list>);
void map_delobject(BlockId, BL type);
@@ -604,7 +573,7 @@ void map_foreachobject(std::function<void(dumb_ptr<block_list>)>,
//
void map_quit(dumb_ptr<map_session_data>);
// npc
-int map_addnpc(map_local *, dumb_ptr<npc_data>);
+int map_addnpc(Borrowed<map_local>, dumb_ptr<npc_data>);
void map_log(XString line);
#define MAP_LOG(format, ...) \
@@ -612,7 +581,7 @@ void map_log(XString line);
#define MAP_LOG_PC(sd, fmt, ...) \
MAP_LOG("PC%d %s:%d,%d " fmt, \
- sd->status_key.char_id, (sd->bl_m ? sd->bl_m->name_ : stringish<MapName>("undefined.gat"_s)), sd->bl_x, sd->bl_y, ## __VA_ARGS__)
+ sd->status_key.char_id, (sd->bl_m->name_), sd->bl_x, sd->bl_y, ## __VA_ARGS__)
// 床アイテム関連
void map_clearflooritem_timer(TimerData *, tick_t, BlockId);
@@ -622,17 +591,15 @@ void map_clearflooritem(BlockId id)
map_clearflooritem_timer(nullptr, tick_t(), id);
}
BlockId map_addflooritem_any(Item *, int amount,
- map_local *m, int x, int y,
+ Borrowed<map_local> m, int x, int y,
dumb_ptr<map_session_data> *owners, interval_t *owner_protection,
interval_t lifetime, int dispersal);
BlockId map_addflooritem(Item *, int,
- map_local *, int, int,
+ Borrowed<map_local>, int, int,
dumb_ptr<map_session_data>, dumb_ptr<map_session_data>,
dumb_ptr<map_session_data>);
// キャラid=>キャラ名 変換関連
-extern
-DMap<BlockId, dumb_ptr<block_list>> id_db;
void map_addchariddb(CharId charid, CharName name);
CharName map_charid2nick(CharId);
@@ -671,8 +638,8 @@ dumb_ptr<magic::invocation> map_id_is_spell(BlockId id)
}
-map_local *map_mapname2mapid(MapName);
-int map_mapname2ipport(MapName, IP4Address *, int *);
+Option<Borrowed<map_local>> map_mapname2mapid(MapName);
+int map_mapname2ipport(MapName, Borrowed<IP4Address>, Borrowed<int>);
int map_setipport(MapName name, IP4Address ip, int port);
void map_addiddb(dumb_ptr<block_list>);
void map_deliddb(dumb_ptr<block_list> bl);
@@ -689,14 +656,14 @@ dumb_ptr<map_session_data> map_get_prev_session(
dumb_ptr<map_session_data> current);
// gat関連
-MapCell map_getcell(map_local *, int, int);
-void map_setcell(map_local *, int, int, MapCell);
+MapCell map_getcell(Borrowed<map_local>, int, int);
+void map_setcell(Borrowed<map_local>, int, int, MapCell);
// その他
bool map_check_dir(DIR s_dir, DIR t_dir);
DIR map_calc_dir(dumb_ptr<block_list> src, int x, int y);
-std::pair<uint16_t, uint16_t> map_randfreecell(map_local *m,
+std::pair<uint16_t, uint16_t> map_randfreecell(Borrowed<map_local> m,
uint16_t x, uint16_t y, uint16_t w, uint16_t h);
inline dumb_ptr<map_session_data> block_list::as_player() { return dumb_ptr<map_session_data>(static_cast<map_session_data *>(this)) ; }
@@ -722,4 +689,14 @@ inline dumb_ptr<npc_data_script> npc_data::is_script() { return npc_subtype == N
inline dumb_ptr<npc_data_shop> npc_data::is_shop() { return npc_subtype == NpcSubtype::SHOP ? as_shop() : nullptr ; }
inline dumb_ptr<npc_data_warp> npc_data::is_warp() { return npc_subtype == NpcSubtype::WARP ? as_warp() : nullptr ; }
inline dumb_ptr<npc_data_message> npc_data::is_message() { return npc_subtype == NpcSubtype::MESSAGE ? as_message() : nullptr ; }
+
+void map_addmap(MapName mapname);
+void map_delmap(MapName mapname);
+
+struct charid2nick
+{
+ CharName nick;
+ int req_id;
+};
+} // namespace map
} // namespace tmwa
diff --git a/src/map/map.py b/src/map/map.py
index cb29d53..c7adf56 100644
--- a/src/map/map.py
+++ b/src/map/map.py
@@ -1,7 +1,7 @@
class map_local(object):
__slots__ = ('_value')
- name = 'tmwa::map_local'
+ name = 'tmwa::map::map_local'
depth = 1
enabled = True
@@ -26,14 +26,14 @@ class map_local(object):
yield '->ys', value['ys']
tests = [
- ('static_cast<tmwa::map_local *>(nullptr)', '(map_local *) nullptr'),
+ ('static_cast<tmwa::map::map_local *>(nullptr)', '(map_local *) nullptr'),
('fake_map_local("map"_s, 42, 404)', '(map_local *) = {->name = "map", ->xs = 42, ->ys = 404}'),
]
class map_remote(object):
__slots__ = ('_value')
- name = 'tmwa::map_remote'
+ name = 'tmwa::map::map_remote'
depth = 1
enabled = True
@@ -58,14 +58,14 @@ class map_remote(object):
yield '->port', value['port']
tests = [
- ('static_cast<tmwa::map_remote *>(nullptr)', '(map_remote *) nullptr'),
+ ('static_cast<tmwa::map::map_remote *>(nullptr)', '(map_remote *) nullptr'),
('fake_map_remote("map"_s, tmwa::IP4Address({8, 8, 8, 8}), 6667)', '(map_remote *) = {->name = "map", ->ip = 8.8.8.8, ->port = 6667}'),
]
class map_abstract(object):
__slots__ = ('_value')
- name = 'tmwa::map_abstract'
+ name = 'tmwa::map::map_abstract'
depth = 1
enabled = True
@@ -79,19 +79,19 @@ class map_abstract(object):
if value is None:
return '(map_abstract *) nullptr'
gat = value.dereference()['gat']
- gat = gat.address.cast(gdb.lookup_type('tmwa::map_abstract').pointer().pointer()).dereference()
+ gat = gat.address.cast(gdb.lookup_type('tmwa::map::map_abstract').pointer().pointer()).dereference()
if gat:
- return value.cast(gdb.lookup_type('tmwa::map_local').pointer())
+ return value.cast(gdb.lookup_type('tmwa::map::map_local').pointer())
else:
- return value.cast(gdb.lookup_type('tmwa::map_remote').pointer())
+ return value.cast(gdb.lookup_type('tmwa::map::map_remote').pointer())
tests = [
- ('static_cast<tmwa::map_abstract *>(nullptr)', '(map_abstract *) nullptr'),
+ ('static_cast<tmwa::map::map_abstract *>(nullptr)', '(map_abstract *) nullptr'),
] + [
- ('static_cast<tmwa::map_abstract *>(%s); value->gat.reset(new tmwa::MapCell[1])' % expr, expected)
+ ('static_cast<tmwa::map::map_abstract *>(%s); value->gat.reset(new tmwa::map::MapCell[1])' % expr, expected)
for (expr, expected) in map_local.tests[1:]
] + [
- ('static_cast<tmwa::map_abstract *>(%s)' % expr, expected)
+ ('static_cast<tmwa::map::map_abstract *>(%s)' % expr, expected)
for (expr, expected) in map_remote.tests[1:]
]
@@ -99,9 +99,9 @@ class map_abstract(object):
using tmwa::operator "" _s;
inline
- tmwa::map_local *fake_map_local(tmwa::ZString name, int xs, int ys)
+ tmwa::map::map_local *fake_map_local(tmwa::ZString name, int xs, int ys)
{
- auto *p = new tmwa::map_local{};
+ auto *p = new tmwa::map::map_local{};
p->name_ = tmwa::stringish<tmwa::MapName>(name);
p->xs = xs;
p->ys = ys;
@@ -109,17 +109,17 @@ class map_abstract(object):
}
inline
- tmwa::map_remote *fake_map_remote(tmwa::ZString name, tmwa::IP4Address ip, uint16_t port)
+ tmwa::map::map_remote *fake_map_remote(tmwa::ZString name, tmwa::IP4Address ip, uint16_t port)
{
- auto *p = new tmwa::map_remote{};
+ auto *p = new tmwa::map::map_remote{};
p->name_ = tmwa::stringish<tmwa::MapName>(name);
p->ip = ip;
p->port = port;
return p;
}
- void fake_delete(tmwa::map_abstract *);
- void fake_delete(tmwa::map_abstract *map)
+ void fake_delete(tmwa::map::map_abstract *);
+ void fake_delete(tmwa::map::map_abstract *map)
{
delete map;
}
diff --git a/src/map/map.t.hpp b/src/map/map.t.hpp
index b475f9b..267c049 100644
--- a/src/map/map.t.hpp
+++ b/src/map/map.t.hpp
@@ -29,11 +29,13 @@
#include "../generic/enum.hpp"
#include "../mmo/ids.hpp"
-#include "../mmo/mmo.hpp"
+#include "../high/mmo.hpp"
namespace tmwa
{
+namespace map
+{
enum class BL : uint8_t
{
NUL,
@@ -191,13 +193,9 @@ ENUM_BITWISE_OPERATORS(MapCell)
}
using e::MapCell;
-struct MobName : VString<23> {};
-struct NpcName : VString<23> {};
-struct ScriptLabel : VString<23> {};
-struct ItemName : VString<23> {};
-
inline
BlockId account_to_block(AccountId a) { return wrap<BlockId>(unwrap<AccountId>(a)); }
inline
AccountId block_to_account(BlockId b) { return wrap<AccountId>(unwrap<BlockId>(b)); }
+} // namespace map
} // namespace tmwa
diff --git a/src/map/mapflag.cpp b/src/map/mapflag.cpp
index be2ae67..9f3c9ab 100644
--- a/src/map/mapflag.cpp
+++ b/src/map/mapflag.cpp
@@ -18,11 +18,15 @@
// 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 "../strings/xstring.hpp"
+
#include "../poison.hpp"
namespace tmwa
{
+namespace map
+{
// because bitfields, that's why
bool MapFlags::get(MapFlag mf) const
@@ -38,7 +42,7 @@ void MapFlags::set(MapFlag mf, bool val)
flags &=~ static_cast<unsigned>(mf);
}
-bool extract(XString str, MapFlag *mf)
+bool impl_extract(XString str, MapFlag *mf)
{
const struct
{
@@ -91,4 +95,5 @@ MapFlag map_flag_from_int(int shift)
{
return static_cast<MapFlag>(1 << shift);
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/mapflag.hpp b/src/map/mapflag.hpp
index 6d046fa..3538c56 100644
--- a/src/map/mapflag.hpp
+++ b/src/map/mapflag.hpp
@@ -22,11 +22,11 @@
#include <cstdint>
-#include "../mmo/extract.hpp" // TODO remove this (requires specializing the *other* half)
-
namespace tmwa
{
+namespace map
+{
// originally from script.cpp
// These are part of the script API, so they can't change ever,
// even though they are silly.
@@ -77,7 +77,8 @@ public:
void set(MapFlag, bool);
};
-bool extract(XString str, MapFlag *mf);
+bool impl_extract(XString str, MapFlag *mf);
MapFlag map_flag_from_int(int shift);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/mapflag.py b/src/map/mapflag.py
index fe5b016..b0a2f24 100644
--- a/src/map/mapflag.py
+++ b/src/map/mapflag.py
@@ -1,6 +1,6 @@
class MapFlags(object):
__slots__ = ('_value')
- name = 'tmwa::MapFlags'
+ name = 'tmwa::map::MapFlags'
enabled = True
def __init__(self, value):
@@ -52,12 +52,12 @@ class MapFlags(object):
('RESAVE', 30),
]
tests = [
- ('reinterpret_cast<const tmwa::MapFlags&>(static_cast<const unsigned int&>(0x80000000))', 'MapFlags(0x80000000)'),
- ('reinterpret_cast<const tmwa::MapFlags&>(static_cast<const unsigned int&>(0xf0000000))', 'MapFlags(TOWN | OUTSIDE | RESAVE | 0x80000000)'),
+ ('reinterpret_cast<const tmwa::map::MapFlags&>(static_cast<const unsigned int&>(0x80000000))', 'MapFlags(0x80000000)'),
+ ('reinterpret_cast<const tmwa::map::MapFlags&>(static_cast<const unsigned int&>(0xf0000000))', 'MapFlags(TOWN | OUTSIDE | RESAVE | 0x80000000)'),
] + [
- ('tmwa::MapFlags(); value.set(tmwa::MapFlag::%s, true)' % n, 'MapFlags(%s)' % n)
+ ('tmwa::map::MapFlags(); value.set(tmwa::map::MapFlag::%s, true)' % n, 'MapFlags(%s)' % n)
for (n, _) in junk
] + [
- ('reinterpret_cast<const tmwa::MapFlags&>(static_cast<const unsigned int&>(1 << %d))' % i, 'MapFlags(%s)' % n)
+ ('reinterpret_cast<const tmwa::map::MapFlags&>(static_cast<const unsigned int&>(1 << %d))' % i, 'MapFlags(%s)' % n)
for (n, i) in junk
]
diff --git a/src/map/mob.cpp b/src/map/mob.cpp
index dd061d0..539b547 100644
--- a/src/map/mob.cpp
+++ b/src/map/mob.cpp
@@ -36,18 +36,20 @@
#include "../generic/random.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
+#include "../io/extract.hpp"
#include "../io/read.hpp"
#include "../net/socket.hpp"
#include "../net/timer.hpp"
#include "../mmo/config_parse.hpp"
-#include "../mmo/extract.hpp"
+#include "../mmo/cxxstdio_enums.hpp"
#include "../mmo/extract_enums.hpp"
#include "battle.hpp"
+#include "battle_conf.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "itemdb.hpp"
#include "map.hpp"
#include "npc.hpp"
@@ -61,6 +63,8 @@
namespace tmwa
{
+namespace map
+{
constexpr interval_t MIN_MOBTHINKTIME = 100_ms;
// Move probability in the negligent mode MOB (rate of 1000 minute)
@@ -68,8 +72,6 @@ constexpr random_::Fraction MOB_LAZYMOVEPERC {50, 1000};
// Warp probability in the negligent mode MOB (rate of 1000 minute)
constexpr random_::Fraction MOB_LAZYWARPPERC {20, 1000};
-static
-struct mob_db_ mob_db[2001];
struct mob_db_& get_mob_db(Species s)
{
return mob_db[unwrap<Species>(s)];
@@ -318,12 +320,12 @@ int mob_gen_exp(mob_db_ *mob)
(2 * mob->attrs[ATTR::LUK] * mob->max_hp / mod_def);
double attack_factor =
(mob->atk1 + mob->atk2 + mob->attrs[ATTR::STR] / 3.0 + mob->attrs[ATTR::DEX] / 2.0 +
- mob->attrs[ATTR::LUK]) * (1872.0 / mob->adelay) / 4;
+ mob->attrs[ATTR::LUK]) * (1872.0 / mob->adelay.count()) / 4;
double dodge_factor =
pow(mob->lv + mob->attrs[ATTR::AGI] + mob->attrs[ATTR::LUK] / 2.0, 4.0 / 3.0);
// TODO s/persuit/pursuit/g sometime when I'm not worried about diffs
double persuit_factor =
- (3 + mob->range) * bool(mob->mode & MobMode::CAN_MOVE) * 1000 / mob->speed;
+ (3 + mob->range) * bool(mob->mode & MobMode::CAN_MOVE) * 1000 / mob->speed.count();
double aggression_factor =
bool(mob->mode & MobMode::AGGRESSIVE)
? 10.0 / 9.0
@@ -331,8 +333,7 @@ int mob_gen_exp(mob_db_ *mob)
int xp = floor(effective_hp * pow(sqrt(attack_factor)
+ sqrt(dodge_factor)
+ sqrt(persuit_factor) + 55, 3)
- * aggression_factor / 2000000.0
- * static_cast<double>(battle_config.base_exp_rate) / 100.);
+ * aggression_factor / 2000000.0);
if (xp < 1)
xp = 1;
PRINTF("Exp for mob '%s' generated: %d\n"_fmt, mob->name, xp);
@@ -357,10 +358,10 @@ void mob_init(dumb_ptr<mob_data> md)
md->stats[mob_stat::LUK] = get_mob_db(mob_class).attrs[ATTR::LUK];
md->stats[mob_stat::ATK1] = get_mob_db(mob_class).atk1;
md->stats[mob_stat::ATK2] = get_mob_db(mob_class).atk2;
- md->stats[mob_stat::ADELAY] = get_mob_db(mob_class).adelay;
+ md->stats[mob_stat::ADELAY] = get_mob_db(mob_class).adelay.count();
md->stats[mob_stat::DEF] = get_mob_db(mob_class).def;
md->stats[mob_stat::MDEF] = get_mob_db(mob_class).mdef;
- md->stats[mob_stat::SPEED] = get_mob_db(mob_class).speed;
+ md->stats[mob_stat::SPEED] = get_mob_db(mob_class).speed.count();
md->stats[mob_stat::XP_BONUS] = MOB_XP_BONUS_BASE;
for (i = 0; i < mutations_nr; i++)
@@ -410,15 +411,15 @@ BlockId mob_once_spawn(dumb_ptr<map_session_data> sd,
NpcEvent event)
{
dumb_ptr<mob_data> md = nullptr;
- map_local *m;
int count;
- if (sd && mapname == MOB_THIS_MAP)
- m = sd->bl_m;
- else
- m = map_mapname2mapid(mapname);
+ P<map_local> m = (
+ (sd && mapname == MOB_THIS_MAP)
+ ? sd->bl_m
+ : TRY_UNWRAP(map_mapname2mapid(mapname), return BlockId())
+ );
- if (m == nullptr || amount <= 0 || mobdb_checkid(mob_class) == Species())
+ if (amount <= 0 || mobdb_checkid(mob_class) == Species())
return BlockId();
if (sd)
@@ -470,18 +471,18 @@ BlockId mob_once_spawn_area(dumb_ptr<map_session_data> sd,
{
int x, y, i, max, lx = -1, ly = -1;
BlockId id;
- map_local *m;
- if (mapname == MOB_THIS_MAP)
- m = sd->bl_m;
- else
- m = map_mapname2mapid(mapname);
+ P<map_local> m = (
+ (mapname == MOB_THIS_MAP)
+ ? sd->bl_m
+ : TRY_UNWRAP(map_mapname2mapid(mapname), return BlockId())
+ );
max = (y1 - y0 + 1) * (x1 - x0 + 1) * 3;
if (max > 1000)
max = 1000;
- if (m == nullptr || amount <= 0 || (mobdb_checkid(mob_class) == Species())) // A summon is stopped if a value is unusual
+ if (amount <= 0 || (mobdb_checkid(mob_class) == Species())) // A summon is stopped if a value is unusual
return BlockId();
for (i = 0; i < amount; i++)
@@ -1148,7 +1149,7 @@ int mob_spawn(BlockId id)
mob_init(md);
if (!md->stats[mob_stat::SPEED])
- md->stats[mob_stat::SPEED] = get_mob_db(md->mob_class).speed;
+ md->stats[mob_stat::SPEED] = get_mob_db(md->mob_class).speed.count();
md->def_ele = get_mob_db(md->mob_class).element;
md->master_id = BlockId();
md->master_dist = 0;
@@ -1178,11 +1179,10 @@ int mob_spawn(BlockId id)
assert (!md->sc_data[i].timer);
md->sc_data[i].val1 = 0;
}
- md->sc_count = 0;
md->opt1 = Opt1::ZERO;
md->opt2 = Opt2::ZERO;
md->opt3 = Opt3::ZERO;
- md->option = Option::ZERO;
+ md->option = Opt0::ZERO;
md->hp = battle_get_max_hp(md);
if (md->hp <= 0)
@@ -1342,7 +1342,7 @@ int mob_target(dumb_ptr<mob_data> md, dumb_ptr<block_list> bl, int dist)
nullpo_retz(bl);
sc_data = battle_get_sc_data(bl);
- Option *option = battle_get_option(bl);
+ Opt0 *option = battle_get_option(bl);
Race race = get_mob_db(md->mob_class).race;
if (md->mode == MobMode::ZERO)
@@ -1572,7 +1572,7 @@ int mob_ai_sub_hard_slavemob(dumb_ptr<mob_data> md, tick_t tick)
// Since it is in the map on which the master is not, teleport is carried out and it pursues.
if (mmd->bl_m != md->bl_m)
{
- mob_warp(md, mmd->bl_m, mmd->bl_x, mmd->bl_y, BeingRemoveWhy::WARPED);
+ mob_warp(md, Some(mmd->bl_m), mmd->bl_x, mmd->bl_y, BeingRemoveWhy::WARPED);
md->state.master_check = 1;
return 0;
}
@@ -1584,7 +1584,7 @@ int mob_ai_sub_hard_slavemob(dumb_ptr<mob_data> md, tick_t tick)
// Since the master was in near immediately before, teleport is carried out and it pursues.
if (old_dist < 10 && md->master_dist > 18)
{
- mob_warp(md, nullptr, mmd->bl_x, mmd->bl_y, BeingRemoveWhy::WARPED);
+ mob_warp(md, None, mmd->bl_x, mmd->bl_y, BeingRemoveWhy::WARPED);
md->state.master_check = 1;
return 0;
}
@@ -2177,7 +2177,7 @@ void mob_ai_lazy(TimerData *, tick_t tick)
*/
struct delay_item_drop
{
- map_local *m;
+ Borrowed<map_local> m = borrow(undefined_gat);
int x, y;
ItemNameId nameid;
int amount;
@@ -2186,7 +2186,7 @@ struct delay_item_drop
struct delay_item_drop2
{
- map_local *m;
+ Borrowed<map_local> m = borrow(undefined_gat);
int x, y;
Item item_data;
dumb_ptr<map_session_data> first_sd, second_sd, third_sd;
@@ -2541,7 +2541,6 @@ int mob_damage(dumb_ptr<block_list> src, dumb_ptr<mob_data> md, int damage,
int base_exp, job_exp, flag = 1;
double per;
- PartyPair p;
// [Fate] The above is the old formula. We do a more involved computation below.
// [o11c] Look in git history for old code, you idiot!
@@ -2592,16 +2591,17 @@ int mob_damage(dumb_ptr<block_list> src, dumb_ptr<mob_data> md, int damage,
);
if (it == ptv.end())
{
- p = party_search(pid);
- if (p && p->exp != 0)
+ Option<PartyPair> p_ = party_search(pid);
+ OMATCH_BEGIN_SOME (p, p_)
{
- DmgLogParty pn {};
- pn.p = p;
- pn.base_exp = base_exp;
- pn.job_exp = job_exp;
- ptv.push_back(pn);
- flag = 0;
+ if (p->exp != 0)
+ {
+ DmgLogParty pn{p, base_exp, job_exp};
+ ptv.push_back(pn);
+ flag = 0;
+ }
}
+ OMATCH_END ();
}
else
{
@@ -2763,7 +2763,7 @@ int mob_warpslave(dumb_ptr<mob_data> md, int x, int y)
* mobワープ
*------------------------------------------
*/
-int mob_warp(dumb_ptr<mob_data> md, map_local *m, int x, int y, BeingRemoveWhy type)
+int mob_warp(dumb_ptr<mob_data> md, Option<Borrowed<map_local>> m_, int x, int y, BeingRemoveWhy type)
{
int i = 0, xs = 0, ys = 0, bx = x, by = y;
@@ -2772,8 +2772,7 @@ int mob_warp(dumb_ptr<mob_data> md, map_local *m, int x, int y, BeingRemoveWhy t
if (md->bl_prev == nullptr)
return 0;
- if (m == nullptr)
- m = md->bl_m;
+ P<map_local> m = m_.copy_or(md->bl_m);
if (type != BeingRemoveWhy::NEGATIVE1)
{
@@ -2892,10 +2891,10 @@ int mob_summonslave(dumb_ptr<mob_data> md2, int *value_, int amount, int flag)
bx = md2->bl_x;
by = md2->bl_y;
- map_local *m = md2->bl_m;
+ P<map_local> m = md2->bl_m;
Species values[5];
- for (count = 0; count < 5 && values[count] != Species(); ++count)
+ for (count = 0; count < 5 && value_[count]; ++count)
values[count] = wrap<Species>(value_[count]);
if (count < 1)
return 0;
@@ -3417,10 +3416,10 @@ int mob_makedummymobdb(Species mob_class)
get_mob_db(mob_class).race = Race::formless;
get_mob_db(mob_class).element = LevelElement{0, Element::neutral};
get_mob_db(mob_class).mode = MobMode::ZERO;
- get_mob_db(mob_class).speed = 300;
- get_mob_db(mob_class).adelay = 1000;
- get_mob_db(mob_class).amotion = 500;
- get_mob_db(mob_class).dmotion = 500;
+ get_mob_db(mob_class).speed = 300_ms;
+ get_mob_db(mob_class).adelay = 1000_ms;
+ get_mob_db(mob_class).amotion = 500_ms;
+ get_mob_db(mob_class).dmotion = 500_ms;
for (i = 0; i < 8; i++)
{
get_mob_db(mob_class).dropitem[i].nameid = ItemNameId();
@@ -3430,7 +3429,7 @@ int mob_makedummymobdb(Species mob_class)
}
static
-bool extract(XString str, LevelElement *le)
+bool impl_extract(XString str, LevelElement *le)
{
int tmp;
if (extract(str, &tmp))
@@ -3531,30 +3530,33 @@ bool mob_readdb(ZString filename)
continue;
}
- // TODO move this lower
- get_mob_db(mob_class) = std::move(mdbv);
-
if (get_mob_db(mob_class).base_exp < 0)
- get_mob_db(mob_class).base_exp = 0;
- else if (get_mob_db(mob_class).base_exp > 0
- && (get_mob_db(mob_class).base_exp *
- battle_config.base_exp_rate / 100 > 1000000000
- || get_mob_db(mob_class).base_exp *
- battle_config.base_exp_rate / 100 < 0))
- get_mob_db(mob_class).base_exp = 1000000000;
- else
- get_mob_db(mob_class).base_exp = get_mob_db(mob_class).base_exp * battle_config.base_exp_rate / 100;
-
+ {
+ PRINTF("bad mob line: Xp needs to be greater than 0. %s\n"_fmt, line);
+ rv = false;
+ continue;
+ }
+ if (get_mob_db(mob_class).base_exp > 1000000000)
+ {
+ PRINTF("bad mob line: Xp needs to be less than 1000000000. %s\n"_fmt, line);
+ rv = false;
+ continue;
+ }
if (get_mob_db(mob_class).job_exp < 0)
- get_mob_db(mob_class).job_exp = 0;
- else if (get_mob_db(mob_class).job_exp > 0
- && (get_mob_db(mob_class).job_exp * battle_config.job_exp_rate /
- 100 > 1000000000
- || get_mob_db(mob_class).job_exp *
- battle_config.job_exp_rate / 100 < 0))
- get_mob_db(mob_class).job_exp = 1000000000;
- else
- get_mob_db(mob_class).job_exp = get_mob_db(mob_class).job_exp * battle_config.job_exp_rate / 100;
+ {
+ PRINTF("bad mob line: Job Xp needs to be greater than 0. %s\n"_fmt, line);
+ rv = false;
+ continue;
+ }
+ if (get_mob_db(mob_class).job_exp > 1000000000)
+ {
+ PRINTF("bad mob line: Job Xp needs to be less than 1000000000. %s\n"_fmt, line);
+ rv = false;
+ continue;
+ }
+
+ // TODO move this lower
+ get_mob_db(mob_class) = std::move(mdbv);
for (int i = 0; i < 8; i++)
{
@@ -3585,7 +3587,7 @@ bool mob_readdb(ZString filename)
}
static
-bool extract(XString str, MobSkillCondition *msc)
+bool impl_extract(XString str, MobSkillCondition *msc)
{
const struct
{
@@ -3609,7 +3611,7 @@ bool extract(XString str, MobSkillCondition *msc)
}
static
-bool extract(XString str, MobSkillState *mss)
+bool impl_extract(XString str, MobSkillState *mss)
{
const struct
{
@@ -3632,7 +3634,7 @@ bool extract(XString str, MobSkillState *mss)
}
static
-bool extract(XString str, MobSkillTarget *mst)
+bool impl_extract(XString str, MobSkillTarget *mst)
{
const struct
{
@@ -3745,4 +3747,5 @@ void do_init_mob2(void)
MIN_MOBTHINKTIME * 10
).detach();
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/mob.hpp b/src/map/mob.hpp
index d0cc07a..6d87228 100644
--- a/src/map/mob.hpp
+++ b/src/map/mob.hpp
@@ -20,24 +20,25 @@
// 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 "fwd.hpp"
-
#include "mob.t.hpp"
-#include "../generic/fwd.hpp"
+#include "fwd.hpp"
+
#include "../generic/enum.hpp"
#include "../generic/random.t.hpp"
#include "../net/timer.t.hpp"
#include "battle.t.hpp"
-#include "clif.t.hpp"
+#include "../mmo/clif.t.hpp"
#include "map.hpp"
-#include "skill.t.hpp"
+#include "../mmo/skill.t.hpp"
namespace tmwa
{
+namespace map
+{
#define ENGLISH_NAME stringish<MobName>("--en--"_s)
#define JAPANESE_NAME stringish<MobName>("--ja--"_s)
#define MOB_THIS_MAP stringish<MapName>("this"_s)
@@ -72,7 +73,7 @@ struct mob_db_
Race race;
LevelElement element;
MobMode mode;
- int speed, adelay, amotion, dmotion;
+ interval_t speed, adelay, amotion, dmotion;
int mutations_nr, mutation_power;
struct
{
@@ -127,7 +128,7 @@ int mob_deleteslave(dumb_ptr<mob_data> md);
int mob_counttargeted(dumb_ptr<mob_data> md, dumb_ptr<block_list> src,
ATK target_lv);
-int mob_warp(dumb_ptr<mob_data> md, map_local *m, int x, int y, BeingRemoveWhy type);
+int mob_warp(dumb_ptr<mob_data> md, Option<Borrowed<map_local>> m, int x, int y, BeingRemoveWhy type);
int mobskill_use(dumb_ptr<mob_data> md, tick_t tick, MobSkillCondition event);
int mobskill_event(dumb_ptr<mob_data> md, BF flag);
@@ -136,4 +137,5 @@ void mobskill_castend_pos(TimerData *tid, tick_t tick, BlockId id);
int mob_summonslave(dumb_ptr<mob_data> md2, int *value, int amount, int flag);
void mob_reload(void);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/mob.t.hpp b/src/map/mob.t.hpp
index 160a8a3..54e7ebe 100644
--- a/src/map/mob.t.hpp
+++ b/src/map/mob.t.hpp
@@ -27,6 +27,8 @@
namespace tmwa
{
+namespace map
+{
enum class MobSkillTarget
{
MST_TARGET = 0,
@@ -61,4 +63,5 @@ enum class MobSkillState : uint8_t
MSS_LOOT,
MSS_CHASE,
};
+} // namespace map
} // namespace tmwa
diff --git a/src/compat/iter.cpp b/src/map/npc-internal.hpp
index b6d6b63..993263f 100644
--- a/src/compat/iter.cpp
+++ b/src/map/npc-internal.hpp
@@ -1,7 +1,9 @@
-#include "iter.hpp"
-// iter.cpp - tools for dealing with iterators
+#pragma once
+// npc-internal.hpp - Noncombatants.
//
-// Copyright © 2012-2014 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -18,9 +20,18 @@
// 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 "../poison.hpp"
+#include "npc.hpp"
+#include "fwd.hpp"
namespace tmwa
{
+namespace map
+{
+struct event_data
+{
+ dumb_ptr<npc_data_script> nd;
+ int pos;
+};
+} // namespace map
} // namespace tmwa
diff --git a/src/map/npc-parse.cpp b/src/map/npc-parse.cpp
new file mode 100644
index 0000000..4d9fcbd
--- /dev/null
+++ b/src/map/npc-parse.cpp
@@ -0,0 +1,807 @@
+#include "npc-parse.hpp"
+// npc-parse.cpp - Noncombatants.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 <list>
+
+#include "../compat/nullpo.hpp"
+
+#include "../strings/astring.hpp"
+#include "../strings/xstring.hpp"
+#include "../strings/literal.hpp"
+
+#include "../generic/enum.hpp"
+
+#include "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
+#include "../io/line.hpp"
+
+#include "../mmo/config_parse.hpp"
+
+#include "../high/extract_mmo.hpp"
+
+#include "../ast/npc.hpp"
+
+#include "battle.hpp"
+#include "battle_conf.hpp"
+#include "clif.hpp"
+#include "globals.hpp"
+#include "itemdb.hpp"
+#include "map.hpp"
+#include "mob.hpp"
+#include "npc-internal.hpp"
+#include "script-parse.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+static
+void npc_clearsrcfile(void)
+{
+ npc_srcs.clear();
+}
+
+void npc_addsrcfile(AString name)
+{
+ if (name == "clear"_s)
+ {
+ npc_clearsrcfile();
+ return;
+ }
+
+ npc_srcs.push_back(name);
+}
+
+void npc_delsrcfile(XString name)
+{
+ if (name == "all"_s)
+ {
+ npc_clearsrcfile();
+ return;
+ }
+
+ for (auto it = npc_srcs.begin(); it != npc_srcs.end(); ++it)
+ {
+ if (*it == name)
+ {
+ npc_srcs.erase(it);
+ return;
+ }
+ }
+}
+
+static
+void register_npc_name(dumb_ptr<npc_data> nd)
+{
+ earray<LString, NpcSubtype, NpcSubtype::COUNT> types //=
+ {{
+ "WARP"_s,
+ "SHOP"_s,
+ "SCRIPT"_s,
+ "MESSAGE"_s,
+ }};
+ if (!nd->name)
+ {
+ if (nd->npc_subtype == NpcSubtype::MESSAGE)
+ return;
+ PRINTF("WARNING: npc with no name:\n%s @ %s,%d,%d\n"_fmt,
+ types[nd->npc_subtype],
+ nd->bl_m->name_, nd->bl_x, nd->bl_y);
+ return;
+ }
+ if (dumb_ptr<npc_data> nd_old = npcs_by_name.get(nd->name))
+ {
+ if (nd->npc_subtype != NpcSubtype::WARP
+ || nd_old->npc_subtype != NpcSubtype::WARP)
+ {
+ PRINTF("WARNING: replacing npc with name: %s\n"_fmt, nd->name);
+ PRINTF("old: %s @ %s,%d,%d\n"_fmt,
+ types[nd_old->npc_subtype],
+ nd_old->bl_m->name_, nd_old->bl_x, nd_old->bl_y);
+ PRINTF("new: %s @ %s,%d,%d\n"_fmt,
+ types[nd->npc_subtype],
+ nd->bl_m->name_, nd->bl_x, nd->bl_y);
+ }
+ }
+ // TODO also check #s ?
+ npcs_by_name.put(nd->name, nd);
+}
+
+// extern for atcommand @addwarp
+bool npc_load_warp(ast::npc::Warp& warp)
+{
+ MapName mapname = warp.m.data;
+ int x = warp.x.data, y = warp.y.data;
+
+ 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), abort());
+
+ dumb_ptr<npc_data_warp> nd;
+ nd.new_();
+ nd->bl_id = npc_get_new_npc_id();
+ nd->n = map_addnpc(m, nd);
+
+ nd->bl_prev = nd->bl_next = nullptr;
+ nd->bl_m = m;
+ nd->bl_x = x;
+ nd->bl_y = y;
+ nd->dir = DIR::S;
+ nd->flag = 0;
+ nd->name = warp.name.data;
+
+ nd->npc_class = WARP_CLASS;
+ nd->speed = 200_ms;
+ nd->option = Opt0::ZERO;
+ nd->opt1 = Opt1::ZERO;
+ nd->opt2 = Opt2::ZERO;
+ nd->opt3 = Opt3::ZERO;
+ nd->warp.name = to_mapname;
+ nd->warp.x = to_x;
+ nd->warp.y = to_y;
+ nd->warp.xs = xs;
+ nd->warp.ys = ys;
+
+ 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_warp++;
+ nd->bl_type = BL::NPC;
+ nd->npc_subtype = NpcSubtype::WARP;
+ map_addblock(nd);
+ clif_spawnnpc(nd);
+ register_npc_name(nd);
+
+ return true;
+}
+
+static
+bool npc_load_shop(ast::npc::Shop& shop)
+{
+ 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;
+ Species npc_class = shop.npc_class.data;
+
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), abort());
+
+ 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_;
+ }));
+
+ back.value = it.data.value.data;
+ if (it.data.value_multiply)
+ {
+ back.value = id->value_buy * back.value;
+ }
+ }
+
+ 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->name = shop.name.data;
+ nd->npc_class = npc_class;
+ nd->speed = 200_ms;
+ nd->option = Opt0::ZERO;
+ nd->opt1 = Opt1::ZERO;
+ nd->opt2 = Opt2::ZERO;
+ nd->opt3 = Opt3::ZERO;
+
+ npc_shop++;
+ nd->bl_type = BL::NPC;
+ nd->npc_subtype = NpcSubtype::SHOP;
+ nd->n = map_addnpc(m, nd);
+ map_addblock(nd);
+ clif_spawnnpc(nd);
+ register_npc_name(nd);
+
+ return true;
+}
+
+static
+bool npc_load_monster(ast::npc::Monster& monster)
+{
+ MapName mapname = monster.m.data;
+ int x = monster.x.data, y = monster.y.data;
+ int xs = monster.xs.data, ys = monster.ys.data;
+
+ 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;
+}
+
+static
+bool npc_load_mapflag(ast::npc::MapFlag& mapflag)
+{
+ MapName mapname = mapflag.m.data;
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), abort());
+
+ MapFlag mf;
+ if (!extract(mapflag.name.data, &mf))
+ {
+ mapflag.name.span.error("No such mapflag"_s);
+ return false;
+ }
+
+ if (mf == MapFlag::NOPVP)
+ {
+ if (mapflag.vec_extra.data.size())
+ {
+ mapflag.vec_extra.span.error("No extra argument expected for mapflag 'nopvp'"_s);
+ return false;
+ }
+ m->flag.set(MapFlag::NOPVP, 1);
+ m->flag.set(MapFlag::PVP, 0);
+ return true;
+ }
+
+ MapName savemap;
+ int savex, savey;
+
+ if (mf == MapFlag::NOSAVE)
+ {
+ 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->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;
+ }
+ }
+ else
+ {
+ 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(STRPRINTF("script function \"%s\""_fmt, script_function.name.data), 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(STRPRINTF("script npc \"%s\""_fmt, script_none.name.data), body, false);
+ if (script == nullptr)
+ return false;
+
+ dumb_ptr<npc_data_script> nd;
+ nd.new_();
+ nd->scr.event_needs_map = false;
+
+ nd->name = script_none.name.data;
+
+ 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 = INVISIBLE_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;
+
+ npc_script++;
+ nd->bl_type = BL::NPC;
+ nd->npc_subtype = NpcSubtype::SCRIPT;
+
+ register_npc_name(nd);
+
+ for (auto& pair : scriptlabel_db)
+ npc_convertlabel_db(pair.first, pair.second, nd);
+
+ for (npc_label_list& el : nd->scr.label_listv)
+ {
+ ScriptLabel lname = el.name;
+ int pos = el.pos;
+
+ if (lname.startswith("On"_s))
+ {
+ struct event_data ev {};
+ ev.nd = nd;
+ ev.pos = pos;
+ NpcEvent buf;
+ buf.npc = nd->name;
+ buf.label = lname;
+ ev_db.insert(buf, ev);
+ }
+ }
+
+ for (npc_label_list& el : nd->scr.label_listv)
+ {
+ int t_ = 0;
+ ScriptLabel lname = el.name;
+ int pos = el.pos;
+ if (lname.startswith("OnTimer"_s) && extract(lname.xslice_t(7), &t_) && t_ > 0)
+ {
+ interval_t t = static_cast<interval_t>(t_);
+
+ npc_timerevent_list tel {};
+ tel.timer = t;
+ tel.pos = pos;
+
+ auto it = std::lower_bound(nd->scr.timer_eventv.begin(), nd->scr.timer_eventv.end(), tel,
+ [](const npc_timerevent_list& l, const npc_timerevent_list& r)
+ {
+ return l.timer < r.timer;
+ }
+ );
+ assert (it == nd->scr.timer_eventv.end() || it->timer != tel.timer);
+
+ nd->scr.timer_eventv.insert(it, std::move(tel));
+ }
+ }
+ // 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(ast::script::ScriptBody& body, ast::npc::ScriptMap& script_map)
+{
+ 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(STRPRINTF("script npc \"%s\""_fmt, script_map.name.data), 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;
+
+ {
+ 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);
+ }
+ }
+
+ nd->scr.xs = xs;
+ nd->scr.ys = ys;
+ nd->scr.event_needs_map = true;
+ }
+
+ nd->name = script_map.name.data;
+
+ 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;
+
+ npc_script++;
+ nd->bl_type = BL::NPC;
+ nd->npc_subtype = NpcSubtype::SCRIPT;
+
+ nd->n = map_addnpc(m, nd);
+ map_addblock(nd);
+
+ clif_spawnnpc(nd);
+
+ register_npc_name(nd);
+
+ for (auto& pair : scriptlabel_db)
+ npc_convertlabel_db(pair.first, pair.second, nd);
+
+ for (npc_label_list& el : nd->scr.label_listv)
+ {
+ ScriptLabel lname = el.name;
+ int pos = el.pos;
+
+ if (lname.startswith("On"_s))
+ {
+ struct event_data ev {};
+ ev.nd = nd;
+ ev.pos = pos;
+ NpcEvent buf;
+ buf.npc = nd->name;
+ buf.label = lname;
+ ev_db.insert(buf, ev);
+ }
+ }
+
+ for (npc_label_list& el : nd->scr.label_listv)
+ {
+ int t_ = 0;
+ ScriptLabel lname = el.name;
+ int pos = el.pos;
+ if (lname.startswith("OnTimer"_s) && extract(lname.xslice_t(7), &t_) && t_ > 0)
+ {
+ interval_t t = static_cast<interval_t>(t_);
+
+ npc_timerevent_list tel {};
+ tel.timer = t;
+ tel.pos = pos;
+
+ auto it = std::lower_bound(nd->scr.timer_eventv.begin(), nd->scr.timer_eventv.end(), tel,
+ [](const npc_timerevent_list& l, const npc_timerevent_list& r)
+ {
+ return l.timer < r.timer;
+ }
+ );
+ assert (it == nd->scr.timer_eventv.end() || it->timer != tel.timer);
+
+ nd->scr.timer_eventv.insert(it, std::move(tel));
+ }
+ }
+ // 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_any(ast::npc::Script *script)
+{
+ MATCH_BEGIN (*script)
+ {
+ MATCH_CASE (ast::npc::ScriptFunction&, script_function)
+ {
+ return npc_load_script_function(script->body, script_function);
+ }
+ MATCH_CASE (ast::npc::ScriptNone&, script_none)
+ {
+ return npc_load_script_none(script->body, script_none);
+ }
+ MATCH_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);
+ }
+ }
+ MATCH_END ();
+ abort();
+}
+
+dumb_ptr<npc_data> npc_spawn_text(Borrowed<map_local> m, int x, int y,
+ Species npc_class, NpcName name, AString message)
+{
+ dumb_ptr<npc_data_message> retval;
+ retval.new_();
+ retval->bl_id = npc_get_new_npc_id();
+ retval->bl_x = x;
+ retval->bl_y = y;
+ retval->bl_m = m;
+ retval->bl_type = BL::NPC;
+ retval->npc_subtype = NpcSubtype::MESSAGE;
+
+ retval->name = name;
+ if (message)
+ retval->message = message;
+
+ retval->npc_class = npc_class;
+ retval->speed = 200_ms;
+
+ clif_spawnnpc(retval);
+ map_addblock(retval);
+ map_addiddb(retval);
+ register_npc_name(retval);
+
+ return retval;
+}
+
+static
+bool load_one_npc(io::LineCharReader& fp, bool& done)
+{
+ 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);
+
+ MATCH_BEGIN (tl)
+ {
+ MATCH_CASE (ast::npc::Comment&, c)
+ {
+ (void)c;
+ return true;
+ }
+ MATCH_CASE (ast::npc::Warp&, warp)
+ {
+ auto& mapname = warp.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_warp(warp);
+ }
+ MATCH_CASE (ast::npc::Shop&, shop)
+ {
+ auto& mapname = shop.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_shop(shop);
+ }
+ MATCH_CASE (ast::npc::Monster&, monster)
+ {
+ auto& mapname = monster.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_monster(monster);
+ }
+ MATCH_CASE (ast::npc::MapFlag&, mapflag)
+ {
+ auto& mapname = mapflag.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_mapflag(mapflag);
+ }
+ MATCH_CASE (ast::npc::Script&, script)
+ {
+ return npc_load_script_any(&script);
+ }
+ }
+ MATCH_END ();
+ 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("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)
+ {
+ PRINTF("Cowardly refusing to continue after %d errors\n"_fmt, script_errors);
+ rv = false;
+ }
+ return rv;
+}
+} // namespace map
+} // namespace tmwa
diff --git a/src/map/npc-parse.hpp b/src/map/npc-parse.hpp
new file mode 100644
index 0000000..9bc3448
--- /dev/null
+++ b/src/map/npc-parse.hpp
@@ -0,0 +1,44 @@
+#pragma once
+// npc-parse.hpp - Noncombatants.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+bool npc_load_warp(ast::npc::Warp& warp);
+
+/**
+ * Spawns and installs a talk-only NPC
+ *
+ * \param message The message to speak. If message is nullptr, the NPC will not do anything at all.
+ */
+dumb_ptr<npc_data> npc_spawn_text(Borrowed<map_local> m, int x, int y,
+ Species class_, NpcName name, AString message);
+
+void npc_addsrcfile(AString name);
+void npc_delsrcfile(XString name);
+bool do_init_npc(void);
+} // namespace map
+} // namespace tmwa
diff --git a/src/map/npc.cpp b/src/map/npc.cpp
index a6427d6..4296432 100644
--- a/src/map/npc.cpp
+++ b/src/map/npc.cpp
@@ -1,4 +1,4 @@
-#include "npc.hpp"
+#include "npc-internal.hpp"
// npc.cpp - Noncombatants.
//
// Copyright © ????-2004 Athena Dev Teams
@@ -38,23 +38,20 @@
#include "../generic/db.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/read.hpp"
+#include "../io/extract.hpp"
#include "../net/timer.hpp"
-#include "../mmo/config_parse.hpp"
-#include "../mmo/extract.hpp"
-#include "../mmo/utils.hpp"
-
#include "../proto2/map-user.hpp"
#include "battle.hpp"
+#include "battle_conf.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "itemdb.hpp"
#include "map.hpp"
-#include "mob.hpp"
#include "pc.hpp"
-#include "script.hpp"
+#include "script-call.hpp"
#include "skill.hpp"
#include "../poison.hpp"
@@ -62,14 +59,8 @@
namespace tmwa
{
-static
-std::list<AString> npc_srcs;
-
-static
-BlockId npc_id = START_NPC_NUM;
-static
-int npc_warp, npc_shop, npc_script, npc_mob;
-
+namespace map
+{
BlockId npc_get_new_npc_id(void)
{
BlockId rv = npc_id;
@@ -77,21 +68,6 @@ BlockId npc_get_new_npc_id(void)
return rv;
}
-struct event_data
-{
- dumb_ptr<npc_data_script> nd;
- int pos;
-};
-static
-Map<NpcEvent, struct event_data> ev_db;
-static
-DMap<NpcName, dumb_ptr<npc_data>> npcs_by_name;
-
-// used for clock-based event triggers
-// only tm_min, tm_hour, and tm_mday are used
-static
-struct tm ev_tm_b;
-
/*==========================================
* NPCの無効化/有効化
* npc_enable
@@ -206,22 +182,6 @@ int npc_delete(dumb_ptr<npc_data> nd)
return 0;
}
-void npc_timer_event(NpcEvent eventname)
-{
- struct event_data *ev = ev_db.search(eventname);
- dumb_ptr<npc_data_script> nd;
-// int xs,ys;
-
- if ((ev == nullptr || (nd = ev->nd) == nullptr))
- {
- PRINTF("npc_event: event not found [%s]\n"_fmt,
- eventname);
- return;
- }
-
- run_script(ScriptPointer(nd->scr.script.get(), ev->pos), nd->bl_id, nd->bl_id);
-}
-
/*==========================================
* 全てのNPCのOn*イベント実行
*------------------------------------------
@@ -236,7 +196,7 @@ void npc_event_doall_sub(NpcEvent key, struct event_data *ev,
if (name == p)
{
- run_script_l(ScriptPointer(ev->nd->scr.script.get(), ev->pos), rid, ev->nd->bl_id,
+ run_script_l(ScriptPointer(borrow(*ev->nd->scr.script), ev->pos), rid, ev->nd->bl_id,
argv);
(*c)++;
}
@@ -259,7 +219,7 @@ void npc_event_do_sub(NpcEvent key, struct event_data *ev,
if (name == key)
{
- run_script_l(ScriptPointer(ev->nd->scr.script.get(), ev->pos), rid, ev->nd->bl_id,
+ run_script_l(ScriptPointer(borrow(*ev->nd->scr.script), ev->pos), rid, ev->nd->bl_id,
argv);
(*c)++;
}
@@ -353,7 +313,7 @@ void npc_timerevent(TimerData *, tick_t tick, BlockId id, interval_t data)
id, next));
}
- run_script(ScriptPointer(nd->scr.script.get(), te->pos), BlockId(), nd->bl_id);
+ run_script(ScriptPointer(borrow(*nd->scr.script), te->pos), BlockId(), nd->bl_id);
}
/// Start (or resume) counting ticks to the next npc_timerevent.
@@ -465,39 +425,36 @@ void npc_settimerevent_tick(dumb_ptr<npc_data_script> nd, interval_t newtimer)
int npc_event(dumb_ptr<map_session_data> sd, NpcEvent eventname,
int mob_kill)
{
- struct event_data *ev = ev_db.search(eventname);
+ Option<P<struct event_data>> ev_ = ev_db.search(eventname);
dumb_ptr<npc_data_script> nd;
- int xs, ys;
if (sd == nullptr)
{
PRINTF("npc_event nullpo?\n"_fmt);
}
- if (ev == nullptr && eventname.label == stringish<ScriptLabel>("OnTouch"_s))
+ if (ev_.is_none() && eventname.label == stringish<ScriptLabel>("OnTouch"_s))
return 1;
- if (ev == nullptr || (nd = ev->nd) == nullptr)
+ P<struct event_data> ev = TRY_UNWRAP(ev_,
{
- if (mob_kill)
- {
- {
- return 0;
- }
- }
- else
- {
- if (battle_config.error_log)
- PRINTF("npc_event: event not found [%s]\n"_fmt,
- eventname);
- return 0;
- }
+ if (!mob_kill && battle_config.error_log)
+ PRINTF("npc_event: event not found [%s]\n"_fmt,
+ eventname);
+ return 0;
+ });
+ if ((nd = ev->nd) == nullptr)
+ {
+ if (!mob_kill && battle_config.error_log)
+ PRINTF("npc_event: event not found [%s]\n"_fmt,
+ eventname);
+ return 0;
}
- xs = nd->scr.xs;
- ys = nd->scr.ys;
- if (xs >= 0 && ys >= 0)
+ if (nd->scr.event_needs_map)
{
+ int xs = nd->scr.xs;
+ int ys = nd->scr.ys;
if (nd->bl_m != sd->bl_m)
return 1;
if (xs > 0
@@ -521,28 +478,7 @@ int npc_event(dumb_ptr<map_session_data> sd, NpcEvent eventname,
sd->npc_id = nd->bl_id;
sd->npc_pos =
- run_script(ScriptPointer(nd->scr.script.get(), ev->pos), sd->bl_id, nd->bl_id);
- return 0;
-}
-
-static
-void npc_command_sub(NpcEvent key, struct event_data *ev, NpcName npcname, XString command)
-{
- if (ev->nd->name == npcname
- && key.label.startswith("OnCommand"_s))
- {
- XString temp = key.label.xslice_t(9);
-
- if (command == temp)
- run_script(ScriptPointer(ev->nd->scr.script.get(), ev->pos), BlockId(), ev->nd->bl_id);
- }
-}
-
-int npc_command(dumb_ptr<map_session_data>, NpcName npcname, XString command)
-{
- for (auto& pair : ev_db)
- npc_command_sub(pair.first, &pair.second, npcname, command);
-
+ run_script(ScriptPointer(borrow(*nd->scr.script), ev->pos), sd->bl_id, nd->bl_id);
return 0;
}
@@ -550,7 +486,7 @@ int npc_command(dumb_ptr<map_session_data>, NpcName npcname, XString command)
* 接触型のNPC処理
*------------------------------------------
*/
-int npc_touch_areanpc(dumb_ptr<map_session_data> sd, map_local *m, int x, int y)
+int npc_touch_areanpc(dumb_ptr<map_session_data> sd, Borrowed<map_local> m, int x, int y)
{
int i, f = 1;
int xs, ys;
@@ -647,7 +583,7 @@ int npc_checknear(dumb_ptr<map_session_data> sd, BlockId id)
if (nd->bl_type != BL::NPC)
return 1;
- if (nd->npc_class == NEGATIVE_SPECIES)
+ if (nd->npc_class == INVISIBLE_CLASS)
return 0;
// エリア判定
@@ -696,7 +632,7 @@ int npc_click(dumb_ptr<map_session_data> sd, BlockId id)
npc_event_dequeue(sd);
break;
case NpcSubtype::SCRIPT:
- sd->npc_pos = run_script(ScriptPointer(nd->is_script()->scr.script.get(), 0), sd->bl_id, id);
+ sd->npc_pos = run_script(ScriptPointer(borrow(*nd->is_script()->scr.script), 0), sd->bl_id, id);
break;
case NpcSubtype::MESSAGE:
if (nd->is_message()->message)
@@ -737,7 +673,7 @@ int npc_scriptcont(dumb_ptr<map_session_data> sd, BlockId id)
return 0;
}
- sd->npc_pos = run_script(ScriptPointer(nd->is_script()->scr.script.get(), sd->npc_pos), sd->bl_id, id);
+ sd->npc_pos = run_script(ScriptPointer(borrow(*nd->is_script()->scr.script), sd->npc_pos), sd->bl_id, id);
return 0;
}
@@ -848,8 +784,7 @@ int npc_buylist(dumb_ptr<map_session_data> sd,
const uint16_t& item_l_count = item_list[i].count;
const ItemNameId& item_l_id = item_list[i].name_id;
- struct item_data *item_data;
- if ((item_data = itemdb_exists(item_l_id)) != nullptr)
+ P<struct item_data> item_data = TRY_UNWRAP(itemdb_exists(item_l_id), continue);
{
int amount = item_l_count;
Item item_tmp {};
@@ -918,718 +853,6 @@ int npc_selllist(dumb_ptr<map_session_data> sd,
}
-//
-// 初期化関係
-//
-
-/*==========================================
- * 読み込むnpcファイルのクリア
- *------------------------------------------
- */
-static
-void npc_clearsrcfile(void)
-{
- npc_srcs.clear();
-}
-
-/*==========================================
- * 読み込むnpcファイルの追加
- *------------------------------------------
- */
-void npc_addsrcfile(AString name)
-{
- if (name == "clear"_s)
- {
- npc_clearsrcfile();
- return;
- }
-
- npc_srcs.push_back(name);
-}
-
-/*==========================================
- * 読み込むnpcファイルの削除
- *------------------------------------------
- */
-void npc_delsrcfile(XString name)
-{
- if (name == "all"_s)
- {
- npc_clearsrcfile();
- return;
- }
-
- for (auto it = npc_srcs.begin(); it != npc_srcs.end(); ++it)
- {
- if (*it == name)
- {
- npc_srcs.erase(it);
- return;
- }
- }
-}
-
-static
-void register_npc_name(dumb_ptr<npc_data> nd)
-{
- earray<LString, NpcSubtype, NpcSubtype::COUNT> types //=
- {{
- "WARP"_s,
- "SHOP"_s,
- "SCRIPT"_s,
- "MESSAGE"_s,
- }};
- if (!nd->name)
- {
- if (nd->npc_subtype == NpcSubtype::MESSAGE)
- return;
- PRINTF("WARNING: npc with no name:\n%s @ %s,%d,%d\n"_fmt,
- types[nd->npc_subtype],
- nd->bl_m->name_, nd->bl_x, nd->bl_y);
- return;
- }
- if (dumb_ptr<npc_data> nd_old = npcs_by_name.get(nd->name))
- {
- if (nd->npc_subtype != NpcSubtype::WARP
- || nd_old->npc_subtype != NpcSubtype::WARP)
- {
- PRINTF("WARNING: replacing npc with name: %s\n"_fmt, nd->name);
- PRINTF("old: %s @ %s,%d,%d\n"_fmt,
- types[nd_old->npc_subtype],
- nd_old->bl_m->name_, nd_old->bl_x, nd_old->bl_y);
- PRINTF("new: %s @ %s,%d,%d\n"_fmt,
- types[nd->npc_subtype],
- nd->bl_m->name_, nd->bl_x, nd->bl_y);
- }
- }
- // TODO also check #s ?
- npcs_by_name.put(nd->name, nd);
-}
-
-/*==========================================
- * warp行解析
- *------------------------------------------
- */
-int npc_parse_warp(XString w1, XString, NpcName w3, XString w4)
-{
- int x, y, xs, ys, to_x, to_y;
- int i, j;
- MapName mapname, to_mapname;
- dumb_ptr<npc_data_warp> nd;
-
- 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;
- }
-
- map_local *m = map_mapname2mapid(mapname);
-
- nd.new_();
- nd->bl_id = npc_get_new_npc_id();
- nd->n = map_addnpc(m, nd);
-
- nd->bl_prev = nd->bl_next = nullptr;
- nd->bl_m = m;
- nd->bl_x = x;
- nd->bl_y = y;
- nd->dir = DIR::S;
- nd->flag = 0;
- nd->name = w3;
-
- if (!battle_config.warp_point_debug)
- nd->npc_class = WARP_CLASS;
- else
- nd->npc_class = WARP_DEBUG_CLASS;
- nd->speed = 200_ms;
- nd->option = Option::ZERO;
- nd->opt1 = Opt1::ZERO;
- 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 (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_warp++;
- nd->bl_type = BL::NPC;
- nd->npc_subtype = NpcSubtype::WARP;
- map_addblock(nd);
- 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;
- struct item_data *id = nullptr;
- if (extract(name_or_id, &itv->nameid) && itv->nameid)
- goto return_true;
-
- id = itemdb_searchname(name_or_id.rstrip());
- if (id == nullptr)
- return false;
- itv->nameid = id->nameid;
- goto return_true;
-
-return_true:
- if (itv->value < 0)
- {
- if (id == nullptr)
- id = itemdb_search(itv->nameid);
- itv->value = id->value_buy * abs(itv->value);
- }
- return true;
-}
-
-/*==========================================
- * shop行解析
- *------------------------------------------
- */
-static
-int npc_parse_shop(XString w1, XString, NpcName w3, ZString w4a)
-{
- int x, y;
- DIR dir;
- MapName mapname;
- 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_);
- map_local *m = map_mapname2mapid(mapname);
-
- 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();
- }
-
- if (nd->shop_items.empty())
- {
- nd.delete_();
- return 1;
- }
-
- 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->name = w3;
- nd->npc_class = npc_class;
- nd->speed = 200_ms;
- nd->option = Option::ZERO;
- nd->opt1 = Opt1::ZERO;
- nd->opt2 = Opt2::ZERO;
- nd->opt3 = Opt3::ZERO;
-
- npc_shop++;
- nd->bl_type = BL::NPC;
- nd->npc_subtype = NpcSubtype::SHOP;
- nd->n = map_addnpc(m, nd);
- map_addblock(nd);
- clif_spawnnpc(nd);
- register_npc_name(nd);
-
- return 0;
-}
-
-/*==========================================
- * NPCのラベルデータコンバート
- *------------------------------------------
- */
-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));
-}
-
-/*==========================================
- * script行解析
- *------------------------------------------
- */
-static
-int npc_parse_script(XString w1, XString w2, NpcName w3, ZString w4,
- XString first_line, io::ReadFile& fp, int *lines)
-{
- int x, y;
- DIR dir = DIR::S;
- map_local *m;
- 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;
-
- if (w1 == "-"_s)
- {
- x = 0;
- y = 0;
- m = nullptr;
- }
- else
- {
- 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(',')))
- {
- PRINTF("bad script line : %s\n"_fmt, w3);
- return 1;
- }
- dir = static_cast<DIR>(dir_);
- m = map_mapname2mapid(mapname);
- }
-
- if (w2 == "script"_s)
- {
- // 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)
- {
- 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';
- }
- 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;
- }
-
- nd.new_();
-
- if (m == nullptr)
- {
- }
- else if (extract(w4, record<','>(&npc_class, &xs, &ys)))
- {
- if (xs >= 0)
- xs = xs * 2 + 1;
- if (ys >= 0)
- ys = ys * 2 + 1;
-
- if (npc_class != NEGATIVE_SPECIES)
- {
-
- 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);
- }
- }
- }
-
- 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;
- }
-
- if (npc_class == NEGATIVE_SPECIES && m != nullptr)
- {
- evflag = 1;
- }
-
- if (w3.contains(':'))
- {
- assert(false && "feature removed"_s);
- abort();
- }
-
- {
- nd->name = w3;
- }
-
- 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 = Option::ZERO;
- nd->opt1 = Opt1::ZERO;
- nd->opt2 = Opt2::ZERO;
- nd->opt3 = Opt3::ZERO;
-
- npc_script++;
- nd->bl_type = BL::NPC;
- nd->npc_subtype = NpcSubtype::SCRIPT;
- if (m != nullptr)
- {
- 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);
- }
- register_npc_name(nd);
-
- for (auto& pair : scriptlabel_db)
- npc_convertlabel_db(pair.first, pair.second, nd);
-
- for (npc_label_list& el : nd->scr.label_listv)
- {
- ScriptLabel lname = el.name;
- int pos = el.pos;
-
- if (lname.startswith("On"_s))
- {
- struct event_data ev {};
- ev.nd = nd;
- ev.pos = pos;
- NpcEvent buf;
- buf.npc = nd->name;
- buf.label = lname;
- ev_db.insert(buf, ev);
- }
- }
-
- //-----------------------------------------
- // ラベルデータからタイマーイベント取り込み
- for (npc_label_list& el : nd->scr.label_listv)
- {
- int t_ = 0;
- ScriptLabel lname = el.name;
- int pos = el.pos;
- if (lname.startswith("OnTimer"_s) && extract(lname.xslice_t(7), &t_) && t_ > 0)
- {
- interval_t t = static_cast<interval_t>(t_);
-
- npc_timerevent_list tel {};
- tel.timer = t;
- tel.pos = pos;
-
- auto it = std::lower_bound(nd->scr.timer_eventv.begin(), nd->scr.timer_eventv.end(), tel,
- [](const npc_timerevent_list& l, const npc_timerevent_list& r)
- {
- return l.timer < r.timer;
- }
- );
- assert (it == nd->scr.timer_eventv.end() || it->timer != tel.timer);
-
- nd->scr.timer_eventv.insert(it, std::move(tel));
- }
- }
- // 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;
-}
-
-/*==========================================
- * function行解析
- *------------------------------------------
- */
-static
-int npc_parse_function(XString, XString, XString w3, ZString,
- XString first_line, io::ReadFile& fp, int *lines)
-{
- MString srcbuf;
- srcbuf += first_line.xislice_t(std::find(first_line.begin(), first_line.end(), '{'));
- int startline = *lines;
-
- 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)
- {
- srcbuf += line.xislice_t(std::find(line.begin(), line.end(), '{'));
- startline = *lines;
- }
- 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;
- }
-
- userfunc_db.put(w3, std::move(script));
-
- return 0;
-}
-
-/*==========================================
- * 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_);
-
- map_local *m = map_mapname2mapid(mapname);
-
- if (num > 1 && battle_config.mob_count_rate != 100)
- {
- if ((num = num * battle_config.mob_count_rate / 100) < 1)
- num = 1;
- }
-
- for (i = 0; i < num; i++)
- {
- md.new_();
-
- 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;
-
- 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 0;
-}
-
-/*==========================================
- * マップフラグ行の解析
- *------------------------------------------
- */
-static
-int npc_parse_mapflag(XString w1, XString, XString w3, ZString w4)
-{
- MapName mapname, savemap;
- int savex, savey;
-
- mapname = stringish<MapName>(w1);
- if (!mapname)
- return 1;
-
- map_local *m = map_mapname2mapid(mapname);
- if (m == nullptr)
- 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)
- {
- if (w4 == "SavePoint"_s)
- {
- m->save.map_ = stringish<MapName>("SavePoint"_s);
- m->save.x = -1;
- m->save.y = -1;
- }
- else if (extract(w4, record<','>(&savemap, &savex, &savey)))
- {
- m->save.map_ = savemap;
- m->save.x = savex;
- m->save.y = savey;
- }
- }
- if (mf == MapFlag::RESAVE)
- {
- if (extract(w4, record<','>(&savemap, &savex, &savey)))
- {
- m->resave.map_ = savemap;
- m->resave.x = savex;
- m->resave.y = savey;
- }
- }
- m->flag.set(mf, true);
-
- return 0;
-}
-
-dumb_ptr<npc_data> npc_spawn_text(map_local *m, int x, int y,
- Species npc_class, NpcName name, AString message)
-{
- dumb_ptr<npc_data_message> retval;
- retval.new_();
- retval->bl_id = npc_get_new_npc_id();
- retval->bl_x = x;
- retval->bl_y = y;
- retval->bl_m = m;
- retval->bl_type = BL::NPC;
- retval->npc_subtype = NpcSubtype::MESSAGE;
-
- retval->name = name;
- if (message)
- retval->message = message;
-
- retval->npc_class = npc_class;
- retval->speed = 200_ms;
-
- clif_spawnnpc(retval);
- map_addblock(retval);
- map_addiddb(retval);
- register_npc_name(retval);
-
- return retval;
-}
-
static
void npc_free_internal(dumb_ptr<npc_data> nd_)
{
@@ -1675,116 +898,5 @@ void npc_free(dumb_ptr<npc_data> nd)
map_delblock(nd);
npc_free_internal(nd);
}
-
-/*==========================================
- * npc初期化
- *------------------------------------------
- */
-bool do_init_npc(void)
-{
- bool rv = true;
- // other fields unused
- ev_tm_b.tm_min = -1;
- ev_tm_b.tm_hour = -1;
- ev_tm_b.tm_mday = -1;
-
- for (; !npc_srcs.empty(); npc_srcs.pop_front())
- {
- AString nsl = npc_srcs.front();
- io::ReadFile fp(nsl);
- if (!fp.is_open())
- {
- PRINTF("file not found : %s\n"_fmt, nsl);
- rv = false;
- continue;
- }
- 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))
- {
- 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));
- map_local *m = map_mapname2mapid(mapname);
- if (m == nullptr)
- {
- // "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);
- npc_parse_warp(w1, w2, npcname, w4z);
- }
- else if (w2 == "shop"_s)
- {
- NpcName npcname = stringish<NpcName>(w3);
- npc_parse_shop(w1, w2, npcname, w4z);
- }
- else if (w2 == "script"_s)
- {
- if (w1 == "function"_s)
- {
- npc_parse_function(w1, w2, w3, w4z,
- w4x, fp, &lines);
- }
- else
- {
- NpcName npcname = stringish<NpcName>(w3);
- npc_parse_script(w1, w2, npcname, w4z,
- w4x, fp, &lines);
- }
- }
- else if (w2 == "monster"_s)
- {
- MobName mobname = stringish<MobName>(w3);
- npc_parse_mob(w1, w2, mobname, w4z);
- }
- else if (w2 == "mapflag"_s)
- {
- npc_parse_mapflag(w1, w2, w3, w4z);
- }
- else
- {
- PRINTF("odd script line: %s\n"_fmt, zline);
- script_errors++;
- }
- }
- fflush(stdout);
- }
- PRINTF("\rNPCs 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)
- {
- PRINTF("Cowardly refusing to continue after %d errors\n"_fmt, script_errors);
- rv = false;
- }
- return rv;
-}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/npc.hpp b/src/map/npc.hpp
index 33dd378..b587f5f 100644
--- a/src/map/npc.hpp
+++ b/src/map/npc.hpp
@@ -24,38 +24,33 @@
#include <cstdint>
-#include "../strings/fwd.hpp"
-
-#include "../generic/fwd.hpp"
+#include "../range/slice.hpp"
#include "../net/timer.t.hpp"
-#include "../proto2/fwd.hpp"
-
#include "map.hpp"
+#include "script-call.t.hpp"
namespace tmwa
{
+namespace map
+{
constexpr BlockId START_NPC_NUM = wrap<BlockId>(110000000);
// TODO make these species, see npc_class in npc_data
constexpr Species WARP_CLASS = wrap<Species>(45);
constexpr Species FAKE_NPC_CLASS = wrap<Species>(127);
-constexpr Species WARP_DEBUG_CLASS = wrap<Species>(722);
constexpr Species INVISIBLE_CLASS = wrap<Species>(32767);
int npc_event_dequeue(dumb_ptr<map_session_data> sd);
int npc_event(dumb_ptr<map_session_data> sd, NpcEvent npcname, int);
-void npc_timer_event(NpcEvent eventname); // Added by RoVeRT
-int npc_command(dumb_ptr<map_session_data> sd, NpcName npcname, XString command);
-int npc_touch_areanpc(dumb_ptr<map_session_data>, map_local *, int, int);
+int npc_touch_areanpc(dumb_ptr<map_session_data>, Borrowed<map_local>, int, int);
int npc_click(dumb_ptr<map_session_data>, BlockId);
int npc_scriptcont(dumb_ptr<map_session_data>, BlockId);
int npc_buysellsel(dumb_ptr<map_session_data>, BlockId, int);
int npc_buylist(dumb_ptr<map_session_data>, const std::vector<Packet_Repeat<0x00c8>>&);
int npc_selllist(dumb_ptr<map_session_data>, const std::vector<Packet_Repeat<0x00c9>>&);
-int npc_parse_warp(XString w1, XString, NpcName w3, XString w4);
int npc_enable(NpcName name, bool flag);
dumb_ptr<npc_data> npc_name2id(NpcName name);
@@ -63,21 +58,10 @@ dumb_ptr<npc_data> npc_name2id(NpcName name);
BlockId npc_get_new_npc_id(void);
/**
- * Spawns and installs a talk-only NPC
- *
- * \param message The message to speak. If message is nullptr, the NPC will not do anything at all.
- */
-dumb_ptr<npc_data> npc_spawn_text(map_local *m, int x, int y,
- Species class_, NpcName name, AString message);
-
-/**
* Uninstalls and frees an NPC
*/
void npc_free(dumb_ptr<npc_data> npc);
-void npc_addsrcfile(AString);
-void npc_delsrcfile(XString);
-bool do_init_npc(void);
int npc_event_do_oninit(void);
int npc_event_doall_l(ScriptLabel name, BlockId rid, Slice<argrec_t> argv);
@@ -98,4 +82,5 @@ void npc_timerevent_stop(dumb_ptr<npc_data_script> nd);
interval_t npc_gettimerevent_tick(dumb_ptr<npc_data_script> nd);
void npc_settimerevent_tick(dumb_ptr<npc_data_script> nd, interval_t newtimer);
int npc_delete(dumb_ptr<npc_data> nd);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/party.cpp b/src/map/party.cpp
index 8713c60..ccbfd75 100644
--- a/src/map/party.cpp
+++ b/src/map/party.cpp
@@ -32,10 +32,12 @@
#include "../net/timer.hpp"
#include "../mmo/ids.hpp"
-#include "../mmo/mmo.hpp"
+#include "../high/mmo.hpp"
#include "battle.hpp"
+#include "battle_conf.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "intif.hpp"
#include "map.hpp"
#include "pc.hpp"
@@ -45,13 +47,12 @@
namespace tmwa
{
+namespace map
+{
// 座標やHP送信の間隔
constexpr interval_t PARTY_SEND_XYHP_INVERVAL = 1_s;
static
-Map<PartyId, PartyMost> party_db;
-
-static
void party_check_conflict(dumb_ptr<map_session_data> sd);
static
void party_send_xyhp_timer(TimerData *tid, tick_t tick);
@@ -66,32 +67,30 @@ void do_init_party(void)
}
// 検索
-PartyPair party_search(PartyId party_id)
+Option<PartyPair> party_search(PartyId party_id)
{
- PartyPair p;
- p.party_most = party_db.search(party_id);
- if (p)
- p.party_id = party_id;
- return p;
+ Option<P<PartyMost>> party_most_ = party_db.search(party_id);
+ return party_most_.map([party_id](P<PartyMost> party_most)
+ {
+ return PartyPair{party_id, party_most};
+ });
}
static
-void party_searchname_sub(PartyPair p, PartyName str, PartyPair *dst)
+void party_searchname_sub(PartyPair p, PartyName str, Borrowed<Option<PartyPair>> dst)
{
if (p->name == str)
- *dst = p;
+ *dst = Some(p);
}
// パーティ名検索
-PartyPair party_searchname(PartyName str)
+Option<PartyPair> party_searchname(PartyName str)
{
- PartyPair p;
+ Option<PartyPair> p = None;
for (auto& pair : party_db)
{
- PartyPair tmp;
- tmp.party_id = pair.first;
- tmp.party_most = &pair.second;
- party_searchname_sub(tmp, str, &p);
+ PartyPair tmp{pair.first, borrow(pair.second)};
+ party_searchname_sub(tmp, str, borrow(p));
}
return p;
}
@@ -129,15 +128,13 @@ void party_created(AccountId account_id, int fail, PartyId party_id, PartyName n
{
sd->status.party_id = party_id;
- PartyPair p = party_search(party_id);
- if (p)
+ if (party_search(party_id).is_some())
{
PRINTF("party_created(): ID already exists!\n"_fmt);
exit(1);
}
- p.party_most = party_db.init(party_id);
- p.party_id = party_id;
+ Borrowed<PartyMost> p = party_db.init(party_id);
p->name = name;
/* The party was created successfully. */
@@ -158,8 +155,6 @@ void party_request_info(PartyId party_id)
static
int party_check_member(PartyPair p)
{
- nullpo_retz(p);
-
for (io::FD i : iter_fds())
{
Session *s = get_session(i);
@@ -215,24 +210,32 @@ int party_recv_noinfo(PartyId party_id)
return 0;
}
-// 情報所得
-int party_recv_info(const PartyPair sp)
+static
+PartyPair handle_info(const PartyPair sp)
{
- int i;
-
- nullpo_retz(sp);
-
- PartyPair p = party_search(sp.party_id);
- if (!p)
+ Option<PartyPair> p_ = party_search(sp.party_id);
+ OMATCH_BEGIN_SOME (p, p_)
+ {
+ *p.party_most = *sp.party_most;
+ return p;
+ }
+ OMATCH_END ();
{
- p.party_most = party_db.init(sp.party_id);
+ PartyPair p{sp.party_id, party_db.init(sp.party_id)};
// 最初のロードなのでユーザーのチェックを行う
*p.party_most = *sp.party_most;
party_check_member(p);
+ return p;
}
- else
- *p.party_most = *sp.party_most;
+}
+
+// 情報所得
+int party_recv_info(const PartyPair sp)
+{
+ int i;
+
+ PartyPair p = handle_info(sp);
for (i = 0; i < MAX_PARTY; i++)
{ // sdの設定
@@ -261,13 +264,13 @@ int party_recv_info(const PartyPair sp)
int party_invite(dumb_ptr<map_session_data> sd, AccountId account_id)
{
dumb_ptr<map_session_data> tsd = map_id2sd(account_to_block(account_id));
- PartyPair p = party_search(sd->status.party_id);
+ PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return 0);
int i;
int full = 1; /* Indicates whether or not there's room for one more. */
nullpo_retz(sd);
- if (!tsd || !p || !tsd->sess)
+ if (!tsd || !tsd->sess)
return 0;
if (!battle_config.invite_request_check)
@@ -359,7 +362,6 @@ int party_reply_invite(dumb_ptr<map_session_data> sd, AccountId account_id, int
int party_member_added(PartyId party_id, AccountId account_id, int flag)
{
dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(account_id)), sd2;
- PartyPair p = party_search(party_id);
if (sd == nullptr)
{
@@ -376,12 +378,12 @@ int party_member_added(PartyId party_id, AccountId account_id, int flag)
sd->party_invite = PartyId();
sd->party_invite_account = AccountId();
- if (!p)
+ PartyPair p = TRY_UNWRAP(party_search(party_id),
{
PRINTF("party_member_added: party %d not found.\n"_fmt, party_id);
intif_party_leave(party_id, account_id);
return 0;
- }
+ });
if (flag == 1)
{ // 失敗
@@ -408,13 +410,11 @@ int party_member_added(PartyId party_id, AccountId account_id, int flag)
// パーティ除名要求
int party_removemember(dumb_ptr<map_session_data> sd, AccountId account_id)
{
- PartyPair p;
int i;
nullpo_retz(sd);
- if (!(p = party_search(sd->status.party_id)))
- return 0;
+ PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return 0);
for (i = 0; i < MAX_PARTY; i++)
{ // リーダーかどうかチェック
@@ -439,13 +439,11 @@ int party_removemember(dumb_ptr<map_session_data> sd, AccountId account_id)
// パーティ脱退要求
int party_leave(dumb_ptr<map_session_data> sd)
{
- PartyPair p;
int i;
nullpo_retz(sd);
- if (!(p = party_search(sd->status.party_id)))
- return 0;
+ PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return 0);
for (i = 0; i < MAX_PARTY; i++)
{ // 所属しているか
@@ -462,8 +460,8 @@ int party_leave(dumb_ptr<map_session_data> sd)
int party_member_leaved(PartyId party_id, AccountId account_id, CharName name)
{
dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(account_id));
- PartyPair p = party_search(party_id);
- if (p)
+ Option<PartyPair> p_ = party_search(party_id);
+ OMATCH_BEGIN_SOME (p, p_)
{
int i;
for (i = 0; i < MAX_PARTY; i++)
@@ -474,6 +472,7 @@ int party_member_leaved(PartyId party_id, AccountId account_id, CharName name)
p->member[i].sd = nullptr;
}
}
+ OMATCH_END ();
if (sd != nullptr && sd->status.party_id == party_id)
{
sd->status.party_id = PartyId();
@@ -485,10 +484,8 @@ int party_member_leaved(PartyId party_id, AccountId account_id, CharName name)
// パーティ解散通知
int party_broken(PartyId party_id)
{
- PartyPair p;
int i;
- if (!(p = party_search(party_id)))
- return 0;
+ PartyPair p = TRY_UNWRAP(party_search(party_id), return 0);
for (i = 0; i < MAX_PARTY; i++)
{
@@ -508,12 +505,11 @@ int party_broken(PartyId party_id)
// パーティの設定変更要求
int party_changeoption(dumb_ptr<map_session_data> sd, int exp, int item)
{
- PartyPair p;
-
nullpo_retz(sd);
- if (!sd->status.party_id
- || !(p = party_search(sd->status.party_id)))
+ if (!sd->status.party_id)
+ return 0;
+ if (party_search(sd->status.party_id).is_none())
return 0;
intif_party_changeoption(sd->status.party_id, sd->status_key.account_id, exp,
item);
@@ -524,10 +520,8 @@ int party_changeoption(dumb_ptr<map_session_data> sd, int exp, int item)
int party_optionchanged(PartyId party_id, AccountId account_id, int exp, int item,
int flag)
{
- PartyPair p;
dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(account_id));
- if (!(p = party_search(party_id)))
- return 0;
+ PartyPair p = TRY_UNWRAP(party_search(party_id), return 0);
if (!(flag & 0x01))
p->exp = exp;
@@ -541,10 +535,8 @@ int party_optionchanged(PartyId party_id, AccountId account_id, int exp, int ite
void party_recv_movemap(PartyId party_id, AccountId account_id, MapName mapname,
int online, int lv)
{
- PartyPair p;
int i;
- if (!(p = party_search(party_id)))
- return;
+ PartyPair p = TRY_UNWRAP(party_search(party_id), return);
for (i = 0; i < MAX_PARTY; i++)
{
PartyMember *m = &p->member[i];
@@ -584,8 +576,6 @@ void party_recv_movemap(PartyId party_id, AccountId account_id, MapName mapname,
// パーティメンバの移動
int party_send_movemap(dumb_ptr<map_session_data> sd)
{
- PartyPair p;
-
nullpo_retz(sd);
if (!sd->status.party_id)
@@ -599,7 +589,7 @@ int party_send_movemap(dumb_ptr<map_session_data> sd)
party_check_conflict(sd);
// あるならパーティ情報送信
- if ((p = party_search(sd->status.party_id)))
+ PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return 0);
{
party_check_member(p); // 所属を確認する
if (sd->status.party_id == p.party_id)
@@ -616,15 +606,13 @@ int party_send_movemap(dumb_ptr<map_session_data> sd)
// パーティメンバのログアウト
int party_send_logout(dumb_ptr<map_session_data> sd)
{
- PartyPair p;
-
nullpo_retz(sd);
if (sd->status.party_id)
intif_party_changemap(sd, 0);
// sdが無効になるのでパーティ情報から削除
- if ((p = party_search(sd->status.party_id)))
+ PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return 0);
{
int i;
for (i = 0; i < MAX_PARTY; i++)
@@ -646,9 +634,7 @@ void party_send_message(dumb_ptr<map_session_data> sd, XString mes)
// パーティメッセージ受信
void party_recv_message(PartyId party_id, AccountId account_id, XString mes)
{
- PartyPair p;
- if (!(p = party_search(party_id)))
- return;
+ PartyPair p = TRY_UNWRAP(party_search(party_id), return);
clif_party_message(p, account_id, mes);
}
@@ -667,8 +653,6 @@ void party_send_xyhp_timer_sub(PartyPair p)
{
int i;
- nullpo_retv(p);
-
for (i = 0; i < MAX_PARTY; i++)
{
dumb_ptr<map_session_data> sd = dumb_ptr<map_session_data>(p->member[i].sd);
@@ -697,9 +681,7 @@ void party_send_xyhp_timer(TimerData *, tick_t)
{
for (auto& pair : party_db)
{
- PartyPair tmp;
- tmp.party_id = pair.first;
- tmp.party_most = &pair.second;
+ PartyPair tmp{pair.first, borrow(pair.second)};
party_send_xyhp_timer_sub(tmp);
}
}
@@ -709,8 +691,6 @@ void party_send_xy_clear(PartyPair p)
{
int i;
- nullpo_retv(p);
-
for (i = 0; i < MAX_PARTY; i++)
{
dumb_ptr<map_session_data> sd = dumb_ptr<map_session_data>(p->member[i].sd);
@@ -739,13 +719,11 @@ void party_send_hp_check(dumb_ptr<block_list> bl, PartyId party_id, int *flag)
}
// 経験値公平分配
-int party_exp_share(PartyPair p, map_local *mapid, int base_exp, int job_exp)
+int party_exp_share(PartyPair p, Borrowed<map_local> mapid, int base_exp, int job_exp)
{
dumb_ptr<map_session_data> sd;
int i, c;
- nullpo_retz(p);
-
for (i = c = 0; i < MAX_PARTY; i++)
{
sd = dumb_ptr<map_session_data>(p->member[i].sd);
@@ -770,7 +748,6 @@ int party_exp_share(PartyPair p, map_local *mapid, int base_exp, int job_exp)
void party_foreachsamemap(std::function<void(dumb_ptr<block_list>)> func,
dumb_ptr<map_session_data> sd, int type)
{
- PartyPair p;
int i;
int x0, y0, x1, y1;
dumb_ptr<map_session_data> list[MAX_PARTY];
@@ -778,8 +755,7 @@ void party_foreachsamemap(std::function<void(dumb_ptr<block_list>)> func,
nullpo_retv(sd);
- if (!(p = party_search(sd->status.party_id)))
- return;
+ PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return);
x0 = sd->bl_x - AREA_SIZE;
y0 = sd->bl_y - AREA_SIZE;
@@ -807,4 +783,5 @@ void party_foreachsamemap(std::function<void(dumb_ptr<block_list>)> func,
if (list[i]->bl_prev) // 有効かどうかチェック
func(list[i]);
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/party.hpp b/src/map/party.hpp
index 01a8125..669857e 100644
--- a/src/map/party.hpp
+++ b/src/map/party.hpp
@@ -24,18 +24,14 @@
#include <functional>
-#include "../strings/fwd.hpp"
-
-#include "../generic/fwd.hpp"
-
-#include "../mmo/fwd.hpp"
-
namespace tmwa
{
+namespace map
+{
void do_init_party(void);
-PartyPair party_search(PartyId party_id);
-PartyPair party_searchname(PartyName str);
+Option<PartyPair> party_search(PartyId party_id);
+Option<PartyPair> party_searchname(PartyName str);
int party_create(dumb_ptr<map_session_data> sd, PartyName name);
void party_created(AccountId account_id, int fail, PartyId party_id, PartyName name);
@@ -65,8 +61,9 @@ void party_recv_message(PartyId party_id, AccountId account_id, XString mes);
void party_send_xy_clear(PartyPair p);
void party_send_hp_check(dumb_ptr<block_list> bl, PartyId party_id, int *flag);
-int party_exp_share(PartyPair p, map_local *map, int base_exp, int job_exp);
+int party_exp_share(PartyPair p, Borrowed<map_local> map, int base_exp, int job_exp);
void party_foreachsamemap(std::function<void(dumb_ptr<block_list>)> func,
dumb_ptr<map_session_data> sd, int type);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/path.cpp b/src/map/path.cpp
index 6950797..52d20ad 100644
--- a/src/map/path.cpp
+++ b/src/map/path.cpp
@@ -31,7 +31,7 @@
#include "../io/cxxstdio.hpp"
-#include "clif.t.hpp"
+#include "../mmo/clif.t.hpp"
#include "map.hpp"
#include "../poison.hpp"
@@ -39,6 +39,8 @@
namespace tmwa
{
+namespace map
+{
constexpr int MAX_HEAP = 150;
struct tmp_path
{
@@ -212,10 +214,8 @@ int add_path(int *heap, struct tmp_path *tp, int x, int y, int dist,
*------------------------------------------
*/
static
-bool can_place(struct map_local *m, int x, int y)
+bool can_place(Borrowed<struct map_local> m, int x, int y)
{
- nullpo_retz(m);
-
return !bool(read_gatp(m, x, y) & MapCell::UNWALKABLE);
}
@@ -224,10 +224,8 @@ bool can_place(struct map_local *m, int x, int y)
*------------------------------------------
*/
static
-int can_move(struct map_local *m, int x0, int y0, int x1, int y1)
+int can_move(Borrowed<struct map_local> m, int x0, int y0, int x1, int y1)
{
- nullpo_retz(m);
-
if (x0 - x1 < -1 || x0 - x1 > 1 || y0 - y1 < -1 || y0 - y1 > 1)
return 0;
if (x1 < 0 || y1 < 0 || x1 >= m->xs || y1 >= m->ys)
@@ -247,7 +245,7 @@ int can_move(struct map_local *m, int x0, int y0, int x1, int y1)
* path探索 (x0,y0)->(x1,y1)
*------------------------------------------
*/
-int path_search(struct walkpath_data *wpd, map_local *m, int x0, int y0, int x1, int y1, int flag)
+int path_search(struct walkpath_data *wpd, Borrowed<map_local> m, int x0, int y0, int x1, int y1, int flag)
{
int heap[MAX_HEAP + 1];
int i, rp, x, y;
@@ -256,7 +254,7 @@ int path_search(struct walkpath_data *wpd, map_local *m, int x0, int y0, int x1,
nullpo_retz(wpd);
assert (m->gat);
- map_local *md = m;
+ P<map_local> md = m;
if (x1 < 0 || x1 >= md->xs || y1 < 0 || y1 >= md->ys
|| bool(read_gatp(md, x1, y1) & MapCell::UNWALKABLE))
return -1;
@@ -361,4 +359,5 @@ int path_search(struct walkpath_data *wpd, map_local *m, int x0, int y0, int x1,
return -1;
}
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/path.hpp b/src/map/path.hpp
index 3619e2e..f16baaa 100644
--- a/src/map/path.hpp
+++ b/src/map/path.hpp
@@ -25,5 +25,8 @@
namespace tmwa
{
-int path_search(struct walkpath_data *, map_local *, int, int, int, int, int);
+namespace map
+{
+int path_search(struct walkpath_data *, Borrowed<map_local>, int, int, int, int, int);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/pc.cpp b/src/map/pc.cpp
index ada5b9f..6fa35b0 100644
--- a/src/map/pc.cpp
+++ b/src/map/pc.cpp
@@ -37,36 +37,42 @@
#include "../generic/random.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
#include "../io/read.hpp"
-#include "../net/timer.hpp"
+#include "../mmo/cxxstdio_enums.hpp"
-#include "../mmo/utils.hpp"
+#include "../net/timer.hpp"
+#include "../net/timestamp-utils.hpp"
#include "../proto2/char-map.hpp"
#include "atcommand.hpp"
#include "battle.hpp"
+#include "battle_conf.hpp"
#include "chrif.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "intif.hpp"
#include "itemdb.hpp"
#include "magic-stmt.hpp"
#include "map.hpp"
+#include "map_conf.hpp"
#include "npc.hpp"
#include "party.hpp"
#include "path.hpp"
-#include "script.hpp"
+#include "script-call.hpp"
#include "skill.hpp"
#include "storage.hpp"
#include "trade.hpp"
+#include "quest.hpp"
#include "../poison.hpp"
namespace tmwa
{
+namespace map
+{
// PVP順位計算の間隔
constexpr std::chrono::milliseconds PVP_CALCRANK_INTERVAL =
1_s;
@@ -120,7 +126,7 @@ int sp_coefficient_0 = 100;
// coefficients for each weapon type
// (not all used)
static //const
-earray<interval_t, ItemLook, ItemLook::SINGLE_HANDED_COUNT> aspd_base_0 //=
+earray<interval_t, ItemLook, ItemLook::COUNT> aspd_base_0 //=
{{
650_ms,
700_ms,
@@ -254,10 +260,6 @@ earray<EPOS, EQUIP, EQUIP::COUNT> equip_pos //=
EPOS::ARROW,
}};
-// TODO use DMap<>
-static
-std::map<AccountId, GmLevel> gm_accountm;
-
static
int pc_checkoverhp(dumb_ptr<map_session_data> sd);
static
@@ -287,16 +289,12 @@ int pc_iskiller(dumb_ptr<map_session_data> src,
{
nullpo_retz(src);
- if (src->bl_type != BL::PC)
+ if (src->bl_type != BL::PC || target->bl_type != BL::PC)
return 0;
- if (src->special_state.killer)
+ if ((src->state.pvpchannel == 1) && (target->state.pvpchannel == 1) && !src->bl_m->flag.get(MapFlag::NOPVP))
return 1;
-
- if (target->bl_type != BL::PC)
- return 0;
- if (target->special_state.killable)
+ if ((src->state.pvpchannel > 1) && (target->state.pvpchannel == src->state.pvpchannel)) // this one does not respect NOPVP
return 1;
-
return 0;
}
@@ -319,6 +317,33 @@ int distance(int x0, int y0, int x1, int y1)
}
static
+void pc_pvp_timer(TimerData *, tick_t, BlockId id)
+{
+ dumb_ptr<map_session_data> sd = map_id2sd(id);
+
+ assert (sd != nullptr);
+ assert (sd->bl_type == BL::PC);
+}
+
+int pc_setpvptimer(dumb_ptr<map_session_data> sd, interval_t val)
+{
+ nullpo_retz(sd);
+
+ sd->pvp_timer = Timer(gettick() + val,
+ std::bind(pc_pvp_timer, ph::_1, ph::_2,
+ sd->bl_id));
+ return 0;
+}
+
+int pc_delpvptimer(dumb_ptr<map_session_data> sd)
+{
+ nullpo_retz(sd);
+
+ sd->pvp_timer.cancel();
+ return 0;
+}
+
+static
void pc_invincible_timer(TimerData *, tick_t, BlockId id)
{
dumb_ptr<map_session_data> sd = map_id2sd(id);
@@ -377,7 +402,6 @@ int pc_setrestartvalue(dumb_ptr<map_session_data> sd, int type)
clif_updatestatus(sd, SP::SP);
sd->heal_xp = 0; // [Fate] Set gainable xp for healing this player to 0
-
return 0;
}
@@ -464,7 +488,7 @@ void pc_makesavestatus(dumb_ptr<map_session_data> sd)
// セーブ禁止マップだったので指定位置に移動
if (sd->bl_m->flag.get(MapFlag::NOSAVE))
{
- map_local *m = sd->bl_m;
+ P<map_local> m = sd->bl_m;
if (m->save.map_ == "SavePoint"_s)
sd->status.last_point = sd->status.save_point;
else
@@ -505,12 +529,7 @@ EPOS pc_equippoint(dumb_ptr<map_session_data> sd, IOff0 n)
{
nullpo_retr(EPOS::ZERO, sd);
- if (!sd->inventory_data[n])
- return EPOS::ZERO;
-
- EPOS ep = sd->inventory_data[n]->equip;
-
- return ep;
+ return sd->inventory_data[n].pmd_pget(&item_data::equip).copy_or(EPOS::ZERO);
}
static
@@ -521,7 +540,11 @@ int pc_setinventorydata(dumb_ptr<map_session_data> sd)
for (IOff0 i : IOff0::iter())
{
ItemNameId id = sd->status.inventory[i].nameid;
- sd->inventory_data[i] = itemdb_search(id);
+ // If you think you understand this line, you're wrong.
+ // It does not do what you think it does. Rather, you need to
+ // understand it in the context in which it is used. Despite this,
+ // it is quite common for elements to be None.
+ sd->inventory_data[i] = Some(itemdb_search(id));
}
return 0;
}
@@ -531,40 +554,8 @@ int pc_calcweapontype(dumb_ptr<map_session_data> sd)
{
nullpo_retz(sd);
- if (sd->weapontype1 != ItemLook::NONE
- && sd->weapontype2 == ItemLook::NONE)
- sd->status.weapon = sd->weapontype1;
- if (sd->weapontype1 == ItemLook::NONE
- && sd->weapontype2 != ItemLook::NONE)
- sd->status.weapon = sd->weapontype2;
- else if (sd->weapontype1 == ItemLook::BLADE
- && sd->weapontype2 == ItemLook::BLADE)
- sd->status.weapon = ItemLook::DUAL_BLADE;
- else if (sd->weapontype1 == ItemLook::_2
- && sd->weapontype2 == ItemLook::_2)
- sd->status.weapon = ItemLook::DUAL_2;
- else if (sd->weapontype1 == ItemLook::_6
- && sd->weapontype2 == ItemLook::_6)
- sd->status.weapon = ItemLook::DUAL_6;
- else if ((sd->weapontype1 == ItemLook::BLADE
- && sd->weapontype2 == ItemLook::_2)
- || (sd->weapontype1 == ItemLook::_2
- && sd->weapontype2 == ItemLook::BLADE))
- sd->status.weapon = ItemLook::DUAL_12;
- else if (
- (sd->weapontype1 == ItemLook::BLADE
- && sd->weapontype2 == ItemLook::_6)
- || (sd->weapontype1 == ItemLook::_6
- && sd->weapontype2 == ItemLook::BLADE))
- sd->status.weapon = ItemLook::DUAL_16;
- else if (
- (sd->weapontype1 == ItemLook::_2
- && sd->weapontype2 == ItemLook::_6)
- || (sd->weapontype1 == ItemLook::_6
- && sd->weapontype2 == ItemLook::_2))
- sd->status.weapon = ItemLook::DUAL_26;
- else
- sd->status.weapon = sd->weapontype1;
+ // TODO now that there is no calculation here, store only once
+ sd->status.weapon = sd->weapontype1;
return 0;
}
@@ -588,27 +579,30 @@ int pc_setequipindex(dumb_ptr<map_session_data> sd)
sd->equip_index_maybe[j] = i;
if (bool(sd->status.inventory[i].equip & EPOS::WEAPON))
{
- if (sd->inventory_data[i])
- sd->weapontype1 = sd->inventory_data[i]->look;
- else
- sd->weapontype1 = ItemLook::NONE;
+ OMATCH_BEGIN (sd->inventory_data[i])
+ {
+ OMATCH_CASE_SOME (sdidi)
+ {
+ sd->weapontype1 = sdidi->look;
+ }
+ OMATCH_CASE_NONE ()
+ {
+ sd->weapontype1 = ItemLook::NONE;
+ }
+ }
+ OMATCH_END ();
}
if (bool(sd->status.inventory[i].equip & EPOS::SHIELD))
{
- if (sd->inventory_data[i])
+ OMATCH_BEGIN_SOME (sdidi, sd->inventory_data[i])
{
- if (sd->inventory_data[i]->type == ItemType::WEAPON)
+ if (sdidi->type == ItemType::WEAPON)
{
if (sd->status.inventory[i].equip == EPOS::SHIELD)
- sd->weapontype2 = sd->inventory_data[i]->look;
- else
- sd->weapontype2 = ItemLook::NONE;
+ assert(0 && "unreachable - offhand weapons are not supported");
}
- else
- sd->weapontype2 = ItemLook::NONE;
}
- else
- sd->weapontype2 = ItemLook::NONE;
+ OMATCH_END ();
}
}
}
@@ -620,21 +614,18 @@ int pc_setequipindex(dumb_ptr<map_session_data> sd)
static
int pc_isequip(dumb_ptr<map_session_data> sd, IOff0 n)
{
- struct item_data *item;
eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
//転生や養子の場合の元の職業を算出する
nullpo_retz(sd);
- item = sd->inventory_data[n];
sc_data = battle_get_sc_data(sd);
- GmLevel gm_all_equipment = GmLevel::from(static_cast<uint32_t>(battle_config.gm_all_equipment));
+ GmLevel gm_all_equipment = battle_config.gm_all_equipment;
if (gm_all_equipment && pc_isGM(sd).satisfies(gm_all_equipment))
return 1;
- if (item == nullptr)
- return 0;
+ P<struct item_data> item = TRY_UNWRAP(sd->inventory_data[n], return 0);
if (item->sex != SEX::NEUTRAL && sd->status.sex != item->sex)
return 0;
if (item->elv > 0 && sd->status.base_level < item->elv)
@@ -648,12 +639,11 @@ int pc_isequip(dumb_ptr<map_session_data> sd, IOff0 n)
* char鯖から送られてきたステータスを設定
*------------------------------------------
*/
-int pc_authok(AccountId id, int login_id2, TimeT connect_until_time,
+int pc_authok(AccountId id, int login_id2,
short tmw_version, const CharKey *st_key, const CharData *st_data)
{
dumb_ptr<map_session_data> sd = nullptr;
- PartyPair p;
tick_t tick = gettick();
sd = map_id2sd(account_to_block(id));
@@ -681,7 +671,7 @@ int pc_authok(AccountId id, int login_id2, TimeT connect_until_time,
sd->state.connect_new = 1;
sd->bl_prev = sd->bl_next = nullptr;
- sd->weapontype1 = sd->weapontype2 = ItemLook::NONE;
+ sd->weapontype1 = ItemLook::NONE;
sd->speed = DEFAULT_WALK_SPEED;
sd->state.dead_sit = 0;
sd->dir = DIR::S;
@@ -727,21 +717,6 @@ int pc_authok(AccountId id, int login_id2, TimeT connect_until_time,
// sd->sc_data[i].timer = nullptr;
sd->sc_data[i].val1 = 0;
}
- sd->sc_count = 0;
- {
- Option old_option = sd->status.option;
- sd->status.option = Option::ZERO;
-
- // This would leak information.
- // It's better to make it obvious that players can see you.
- if (false && bool(old_option & Option::INVISIBILITY))
- is_atcommand(sd->sess, sd, "@invisible"_s, GmLevel());
-
- if (bool(old_option & Option::HIDE))
- is_atcommand(sd->sess, sd, "@hide"_s, GmLevel());
- // atcommand_hide might already send it, but also might not
- clif_changeoption(sd);
- }
// パーティー関係の初期化
sd->party_sended = 0;
@@ -757,9 +732,24 @@ int pc_authok(AccountId id, int login_id2, TimeT connect_until_time,
pc_setpos(sd, sd->status.last_point.map_, sd->status.last_point.x,
sd->status.last_point.y, BeingRemoveWhy::GONE);
+ {
+ Opt0 old_option = sd->status.option;
+ sd->status.option = Opt0::ZERO;
+
+ // This would leak information.
+ // It's better to make it obvious that players can see you.
+ if (false && bool(old_option & Opt0::INVISIBILITY))
+ is_atcommand(sd->sess, sd, "@invisible"_s, GmLevel());
+
+ if (bool(old_option & Opt0::HIDE))
+ is_atcommand(sd->sess, sd, "@hide"_s, GmLevel());
+ // atcommand_hide might already send it, but also might not
+ clif_changeoption(sd);
+ }
+
// パーティ、ギルドデータの要求
if (sd->status.party_id
- && !(p = party_search(sd->status.party_id)))
+ && party_search(sd->status.party_id).is_none())
party_request_info(sd->status.party_id);
// pvpの設定
@@ -793,27 +783,21 @@ int pc_authok(AccountId id, int login_id2, TimeT connect_until_time,
sd->auto_ban_info.in_progress = 0;
// Initialize antispam vars
- sd->chat_reset_due = TimeT();
+ sd->chat_reset_due = tick_t();
sd->chat_lines_in = sd->chat_total_repeats = 0;
- sd->chat_repeat_reset_due = TimeT();
+ sd->chat_repeat_reset_due = tick_t();
sd->chat_lastmsg = RString();
for (tick_t& t : sd->flood_rates)
t = tick_t();
- sd->packet_flood_reset_due = TimeT();
+ sd->packet_flood_reset_due = tick_t();
sd->packet_flood_in = 0;
- // message of the limited time of the account
- if (connect_until_time)
- {
- timestamp_seconds_buffer buffer;
- stamp_time(buffer, &connect_until_time);
- AString tmpstr = STRPRINTF("Your account time limit is: %s"_fmt, buffer);
-
- clif_wis_message(sd->sess, wisp_server_name, tmpstr);
- }
pc_calcstatus(sd, 1);
+ npc_event_doall_l(stringish<ScriptLabel>("OnPCLoginEvent"_s), sd->bl_id, nullptr);
+ // Init Quest Log
+ clif_sendallquest(sd);
return 0;
}
@@ -830,7 +814,7 @@ void pc_show_motd(dumb_ptr<map_session_data> sd)
clif_displaymessage(sd->sess, "This server is Free Software, for details type @source in chat or use the tmwa-source tool"_s);
sd->state.seen_motd = true;
- io::ReadFile in(motd_txt);
+ io::ReadFile in(map_conf.motd_txt);
if (in.is_open())
{
AString buf;
@@ -865,7 +849,8 @@ int pc_calc_skillpoint(dumb_ptr<map_session_data> sd)
nullpo_retz(sd);
- for (i = 0; i < skill_pool_skills_size; i++) {
+ for (i = 0; i < skill_pool_skills.size(); i++)
+ {
int lv = sd->status.skill[skill_pool_skills[i]].lv;
if (lv)
skill_points += ((lv * (lv - 1)) >> 1) - 1;
@@ -940,6 +925,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
int bl;
int aspd_rate, refinedef = 0;
int str, dstr, dex;
+ int b_pvpchannel = 0;
nullpo_retz(sd);
@@ -968,6 +954,8 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
b_mdef = sd->mdef;
b_mdef2 = sd->mdef2;
b_base_atk = sd->base_atk;
+ if (!pc_isdead(sd) && sd->state.pvpchannel == 1)
+ b_pvpchannel = sd->state.pvpchannel;
sd->max_weight = max_weight_base_0 + sd->status.attrs[ATTR::STR] * 300;
@@ -976,11 +964,11 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
sd->weight = 0;
for (IOff0 i : IOff0::iter())
{
- if (!sd->status.inventory[i].nameid
- || sd->inventory_data[i] == nullptr)
+ if (!sd->status.inventory[i].nameid)
continue;
+ P<struct item_data> sdidi = TRY_UNWRAP(sd->inventory_data[i], continue);
sd->weight +=
- sd->inventory_data[i]->weight *
+ sdidi->weight *
sd->status.inventory[i].amount;
}
// used to fill cart
@@ -1005,7 +993,6 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
sd->status.max_hp = 0;
sd->status.max_sp = 0;
sd->attackrange = 0;
- sd->attackrange_ = 0;
sd->matk1 = 0;
sd->matk2 = 0;
sd->speed = DEFAULT_WALK_SPEED;
@@ -1016,13 +1003,9 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
sd->arrow_atk = 0;
sd->arrow_hit = 0;
sd->arrow_range = 0;
- sd->nhealhp = sd->nhealsp = sd->nshealhp = sd->nshealsp = sd->nsshealhp =
- sd->nsshealsp = 0;
+ sd->nhealhp = sd->nhealsp = 0;
really_memzero_this(&sd->special_state);
- sd->watk_ = 0; //二刀流用(仮)
- sd->watk_2 = 0;
-
sd->aspd_rate = 100;
sd->speed_rate = 100;
sd->hprecov_rate = 100;
@@ -1038,8 +1021,6 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
sd->double_add_rate = sd->perfect_hit_add = 0;
sd->hp_drain_rate = sd->hp_drain_per = sd->sp_drain_rate =
sd->sp_drain_per = 0;
- sd->hp_drain_rate_ = sd->hp_drain_per_ = sd->sp_drain_rate_ =
- sd->sp_drain_per_ = 0;
sd->spellpower_bonus_target = 0;
@@ -1057,13 +1038,14 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
|| sd->equip_index_maybe[EQUIP::LEGS] == index))
continue;
- if (sd->inventory_data[index])
+ OMATCH_BEGIN_SOME (sdidi, sd->inventory_data[index])
{
sd->spellpower_bonus_target +=
- sd->inventory_data[index]->magic_bonus;
+ sdidi->magic_bonus;
// used to apply cards
}
+ OMATCH_END ();
}
#ifdef USE_ASTRAL_SOUL_SKILL
@@ -1092,31 +1074,15 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
&& (sd->equip_index_maybe[EQUIP::TORSO] == index
|| sd->equip_index_maybe[EQUIP::LEGS] == index))
continue;
- if (sd->inventory_data[index])
+ OMATCH_BEGIN_SOME (sdidi, sd->inventory_data[index])
{
- sd->def += sd->inventory_data[index]->def;
- if (sd->inventory_data[index]->type == ItemType::WEAPON)
+ sd->def += sdidi->def;
+ if (sdidi->type == ItemType::WEAPON)
{
if (i == EQUIP::SHIELD
&& sd->status.inventory[index].equip == EPOS::SHIELD)
{
- //二刀流用データ入力
- sd->watk_ += sd->inventory_data[index]->atk;
- sd->watk_2 = 0;
-
- sd->attackrange_ += sd->inventory_data[index]->range;
- sd->state.lr_flag = 1;
- {
- argrec_t arg[2] =
- {
- {"@slotId"_s, static_cast<int>(i)},
- {"@itemId"_s, unwrap<ItemNameId>(sd->inventory_data[index]->nameid)},
- };
- run_script_l(ScriptPointer(sd->inventory_data[index]->equip_script.get(), 0),
- sd->bl_id, BlockId(),
- arg);
- }
- sd->state.lr_flag = 0;
+ assert(0 && "unreachable - offhand weapons are not supported");
}
else
{
@@ -1124,66 +1090,62 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
argrec_t arg[2] =
{
{"@slotId"_s, static_cast<int>(i)},
- {"@itemId"_s, unwrap<ItemNameId>(sd->inventory_data[index]->nameid)},
+ {"@itemId"_s, unwrap<ItemNameId>(sdidi->nameid)},
};
- sd->watk += sd->inventory_data[index]->atk;
+ sd->watk += sdidi->atk;
- sd->attackrange += sd->inventory_data[index]->range;
- run_script_l(ScriptPointer(sd->inventory_data[index]->equip_script.get(), 0),
+ sd->attackrange += sdidi->range;
+ run_script_l(ScriptPointer(borrow(*sdidi->equip_script), 0),
sd->bl_id, BlockId(),
arg);
}
}
- else if (sd->inventory_data[index]->type == ItemType::ARMOR)
+ else if (sdidi->type == ItemType::ARMOR)
{
argrec_t arg[2] =
{
{"@slotId"_s, static_cast<int>(i)},
- {"@itemId"_s, unwrap<ItemNameId>(sd->inventory_data[index]->nameid)},
+ {"@itemId"_s, unwrap<ItemNameId>(sdidi->nameid)},
};
- sd->watk += sd->inventory_data[index]->atk;
- run_script_l(ScriptPointer(sd->inventory_data[index]->equip_script.get(), 0),
+ sd->watk += sdidi->atk;
+ run_script_l(ScriptPointer(borrow(*sdidi->equip_script), 0),
sd->bl_id, BlockId(),
arg);
}
}
+ OMATCH_END ();
}
if (battle_is_unarmed(sd))
{
sd->watk += skill_power(sd, SkillID::TMW_BRAWLING) / 3; // +66 for 200
sd->watk2 += skill_power(sd, SkillID::TMW_BRAWLING) >> 3; // +25 for 200
- sd->watk_ += skill_power(sd, SkillID::TMW_BRAWLING) / 3; // +66 for 200
- sd->watk_2 += skill_power(sd, SkillID::TMW_BRAWLING) >> 3; // +25 for 200
}
IOff0 aidx = sd->equip_index_maybe[EQUIP::ARROW];
if (aidx.ok())
{
IOff0 index = aidx;
- if (sd->inventory_data[index])
+ OMATCH_BEGIN_SOME (sdidi, sd->inventory_data[index])
{ //まだ属性が入っていない
argrec_t arg[2] =
{
{"@slotId"_s, static_cast<int>(EQUIP::ARROW)},
- {"@itemId"_s, unwrap<ItemNameId>(sd->inventory_data[index]->nameid)},
+ {"@itemId"_s, unwrap<ItemNameId>(sdidi->nameid)},
};
- sd->state.lr_flag = 2;
- run_script_l(ScriptPointer(sd->inventory_data[index]->equip_script.get(), 0),
+ sd->state.lr_flag_is_arrow_2 = 1;
+ run_script_l(ScriptPointer(borrow(*sdidi->equip_script), 0),
sd->bl_id, BlockId(),
arg);
- sd->state.lr_flag = 0;
- sd->arrow_atk += sd->inventory_data[index]->atk;
+ sd->state.lr_flag_is_arrow_2 = 0;
+ sd->arrow_atk += sdidi->atk;
}
+ OMATCH_END ();
}
sd->def += (refinedef + 50) / 100;
if (sd->attackrange < 1)
sd->attackrange = 1;
- if (sd->attackrange_ < 1)
- sd->attackrange_ = 1;
- if (sd->attackrange < sd->attackrange_)
- sd->attackrange = sd->attackrange_;
if (sd->status.weapon == ItemLook::BOW)
sd->attackrange += sd->arrow_range;
sd->double_rate += sd->double_add_rate;
@@ -1201,9 +1163,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
for (ATTR attr : ATTRs)
sd->paramc[attr] = std::max(0, sd->status.attrs[attr] + sd->paramb[attr] + sd->parame[attr]);
- if (sd->status.weapon == ItemLook::BOW
- || sd->status.weapon == ItemLook::_13
- || sd->status.weapon == ItemLook::_14)
+ if (sd->status.weapon == ItemLook::BOW)
{
str = sd->paramc[ATTR::DEX];
dex = sd->paramc[ATTR::STR];
@@ -1291,20 +1251,11 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
sd->mdef2 = 1;
// 二刀流 ASPD 修正
- if (sd->status.weapon < ItemLook::SINGLE_HANDED_COUNT)
+ {
sd->aspd += aspd_base_0[sd->status.weapon]
- (sd->paramc[ATTR::AGI] * 4 + sd->paramc[ATTR::DEX])
* aspd_base_0[sd->status.weapon] / 1000;
- else
- sd->aspd += (
- (aspd_base_0[sd->weapontype1]
- - (sd->paramc[ATTR::AGI] * 4 + sd->paramc[ATTR::DEX])
- * aspd_base_0[sd->weapontype1] / 1000)
- + (aspd_base_0[sd->weapontype2]
- - (sd->paramc[ATTR::AGI] * 4 + sd->paramc[ATTR::DEX])
- * aspd_base_0[sd->weapontype2] / 1000)
- )
- * 140 / 200;
+ }
aspd_rate = sd->aspd_rate;
@@ -1366,7 +1317,6 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
}
// スキルやステータス異常による残りのパラメータ補正
- if (sd->sc_count)
{
// ATK/DEF変化形
if (sd->sc_data[StatusChange::SC_POISON].timer) // 毒状態
@@ -1401,7 +1351,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
if (sd->attack_spell_override)
sd->aspd = sd->attack_spell_delay;
- sd->aspd = std::max(sd->aspd, static_cast<interval_t>(battle_config.max_aspd));
+ sd->aspd = std::max(sd->aspd, battle_config.max_aspd);
sd->amotion = sd->aspd;
sd->dmotion = std::chrono::milliseconds(800 - sd->paramc[ATTR::AGI] * 4);
sd->dmotion = std::max(sd->dmotion, 400_ms);
@@ -1478,6 +1428,8 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
clif_updatestatus(sd, SP::HP);
if (b_sp != sd->status.sp)
clif_updatestatus(sd, SP::SP);
+ if (b_pvpchannel != sd->state.pvpchannel)
+ sd->state.pvpchannel = b_pvpchannel;
return 0;
}
@@ -1498,122 +1450,116 @@ int pc_bonus(dumb_ptr<map_session_data> sd, SP type, int val)
case SP::INT:
case SP::DEX:
case SP::LUK:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->parame[sp_to_attr(type)] += val;
break;
#if 0
case SP::ATK1:
- if (!sd->state.lr_flag)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->watk += val;
- else if (sd->state.lr_flag == 1)
- sd->watk_ += val;
break;
#endif
#if 0
case SP::ATK2:
- if (!sd->state.lr_flag)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->watk2 += val;
- else if (sd->state.lr_flag == 1)
- sd->watk_2 += val;
break;
#endif
#if 0
case SP::BASE_ATK:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->base_atk += val;
break;
#endif
#if 0
case SP::MATK1:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->matk1 += val;
break;
#endif
#if 0
case SP::MATK2:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->matk2 += val;
break;
#endif
#if 0
case SP::DEF1:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->def += val;
break;
#endif
case SP::MDEF1:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->mdef += val;
break;
#if 0
case SP::MDEF2:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->mdef += val;
break;
#endif
case SP::HIT:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->hit += val;
else
sd->arrow_hit += val;
break;
case SP::FLEE1:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->flee += val;
break;
#if 0
case SP::FLEE2:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->flee2 += val * 10;
break;
#endif
case SP::CRITICAL:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->critical += val * 10;
else
sd->arrow_cri += val * 10;
break;
case SP::MAXHP:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->status.max_hp += val;
break;
case SP::MAXSP:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->status.max_sp += val;
break;
case SP::MAXHPRATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->hprate += val;
break;
#if 0
case SP::MAXSPRATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->sprate += val;
break;
#endif
#if 0
case SP::SPRATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->dsprate += val;
break;
#endif
case SP::ATTACKRANGE:
- if (!sd->state.lr_flag)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->attackrange += val;
- else if (sd->state.lr_flag == 1)
- sd->attackrange_ += val;
- else if (sd->state.lr_flag == 2)
+ else
sd->arrow_range += val;
break;
#if 0
case SP::ADD_SPEED:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->speed -= val;
break;
#endif
#if 0
case SP::SPEED_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
{
if (sd->speed_rate > 100 - val)
sd->speed_rate = 100 - val;
@@ -1621,17 +1567,17 @@ int pc_bonus(dumb_ptr<map_session_data> sd, SP type, int val)
break;
#endif
case SP::SPEED_ADDRATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->speed_add_rate = sd->speed_add_rate * (100 - val) / 100;
break;
#if 0
case SP::ASPD:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->aspd -= val * 10;
break;
#endif
case SP::ASPD_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
{
if (sd->aspd_rate > 100 - val)
sd->aspd_rate = 100 - val;
@@ -1639,99 +1585,99 @@ int pc_bonus(dumb_ptr<map_session_data> sd, SP type, int val)
break;
#if 0
case SP::ASPD_ADDRATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->aspd_add_rate = sd->aspd_add_rate * (100 - val) / 100;
break;
#endif
case SP::HP_RECOV_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->hprecov_rate += val;
break;
#if 0
case SP::SP_RECOV_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->sprecov_rate += val;
break;
#endif
case SP::CRITICAL_DEF:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->critical_def += val;
break;
#if 0
case SP::DOUBLE_RATE:
- if (sd->state.lr_flag == 0 && sd->double_rate < val)
+ if (!sd->state.lr_flag_is_arrow_2 && sd->double_rate < val)
sd->double_rate = val;
break;
#endif
case SP::DOUBLE_ADD_RATE:
- if (sd->state.lr_flag == 0)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->double_add_rate += val;
break;
#if 0
case SP::MATK_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->matk_rate += val;
break;
#endif
#if 0
case SP::ATK_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->atk_rate += val;
break;
#endif
#if 0
case SP::PERFECT_HIT_RATE:
- if (sd->state.lr_flag != 2 && sd->perfect_hit < val)
+ if (!sd->state.lr_flag_is_arrow_2 && sd->perfect_hit < val)
sd->perfect_hit = val;
break;
#endif
#if 0
case SP::PERFECT_HIT_ADD_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->perfect_hit_add += val;
break;
#endif
#if 0
case SP::CRITICAL_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->critical_rate += val;
break;
#endif
#if 0
case SP::HIT_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->hit_rate += val;
break;
#endif
#if 0
case SP::FLEE_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->flee_rate += val;
break;
#endif
#if 0
case SP::FLEE2_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->flee2_rate += val;
break;
#endif
case SP::DEF_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->def_rate += val;
break;
case SP::DEF2_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->def2_rate += val;
break;
#if 0
case SP::MDEF_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->mdef_rate += val;
break;
#endif
#if 0
case SP::MDEF2_RATE:
- if (sd->state.lr_flag != 2)
+ if (!sd->state.lr_flag_is_arrow_2)
sd->mdef2_rate += val;
break;
#endif
@@ -1758,29 +1704,19 @@ int pc_bonus2(dumb_ptr<map_session_data> sd, SP type, int type2, int val)
switch (type)
{
case SP::HP_DRAIN_RATE:
- if (!sd->state.lr_flag)
+ if (!sd->state.lr_flag_is_arrow_2)
{
sd->hp_drain_rate += type2;
sd->hp_drain_per += val;
}
- else if (sd->state.lr_flag == 1)
- {
- sd->hp_drain_rate_ += type2;
- sd->hp_drain_per_ += val;
- }
break;
#if 0
case SP::SP_DRAIN_RATE:
- if (!sd->state.lr_flag)
+ if (!sd->state.lr_flag_is_arrow_2)
{
sd->sp_drain_rate += type2;
sd->sp_drain_per += val;
}
- else if (sd->state.lr_flag == 1)
- {
- sd->sp_drain_rate_ += type2;
- sd->sp_drain_per_ += val;
- }
break;
#endif
default:
@@ -1971,7 +1907,6 @@ int pc_remove_items(dumb_ptr<map_session_data> player, ItemNameId item_id, int c
PickupFail pc_additem(dumb_ptr<map_session_data> sd, Item *item_data,
int amount)
{
- struct item_data *data;
int w;
MAP_LOG_PC(sd, "PICKUP %d %d"_fmt, item_data->nameid, amount);
@@ -1981,7 +1916,7 @@ PickupFail pc_additem(dumb_ptr<map_session_data> sd, Item *item_data,
if (!item_data->nameid || amount <= 0)
return PickupFail::BAD_ITEM;
- data = itemdb_search(item_data->nameid);
+ P<struct item_data> data = itemdb_search(item_data->nameid);
if ((w = data->weight * amount) + sd->weight > sd->max_weight)
return PickupFail::TOO_HEAVY;
@@ -2013,7 +1948,7 @@ PickupFail pc_additem(dumb_ptr<map_session_data> sd, Item *item_data,
sd->status.inventory[i].equip = EPOS::ZERO;
sd->status.inventory[i].amount = amount;
- sd->inventory_data[i] = data;
+ sd->inventory_data[i] = Some(data);
clif_additem(sd, i, amount, PickupFail::OKAY);
}
else
@@ -2037,18 +1972,18 @@ int pc_delitem(dumb_ptr<map_session_data> sd, IOff0 n, int amount, int type)
trade_tradecancel(sd);
if (!sd->status.inventory[n].nameid || amount <= 0
- || sd->status.inventory[n].amount < amount
- || sd->inventory_data[n] == nullptr)
+ || sd->status.inventory[n].amount < amount)
return 1;
+ P<struct item_data> sdidn = TRY_UNWRAP(sd->inventory_data[n], return 1);
sd->status.inventory[n].amount -= amount;
- sd->weight -= sd->inventory_data[n]->weight * amount;
+ sd->weight -= sdidn->weight * amount;
if (sd->status.inventory[n].amount <= 0)
{
if (bool(sd->status.inventory[n].equip))
pc_unequipitem(sd, n, CalcStatus::NOW);
sd->status.inventory[n] = Item{};
- sd->inventory_data[n] = nullptr;
+ sd->inventory_data[n] = None;
}
if (!(type & 1))
clif_delitem(sd, n, amount);
@@ -2097,8 +2032,6 @@ int pc_dropitem(dumb_ptr<map_session_data> sd, IOff0 n, int amount)
static
int can_pick_item_up_from(dumb_ptr<map_session_data> self, BlockId other_id)
{
- PartyPair p = party_search(self->status.party_id);
-
/* From ourselves or from no-one? */
if (!self || self->bl_id == other_id || !other_id)
return 1;
@@ -2114,9 +2047,10 @@ int can_pick_item_up_from(dumb_ptr<map_session_data> self, BlockId other_id)
return 1;
/* From a party member? */
+ Option<PartyPair> p = party_search(self->status.party_id);
if (self->status.party_id
&& self->status.party_id == other->status.party_id
- && p && p->item != 0)
+ && p.pmd_pget(&PartyMost::item).copy_or(0) != 0)
return 1;
/* From someone who is far away? */
@@ -2192,16 +2126,13 @@ int pc_takeitem(dumb_ptr<map_session_data> sd, dumb_ptr<flooritem_data> fitem)
static
int pc_isUseitem(dumb_ptr<map_session_data> sd, IOff0 n)
{
- struct item_data *item;
ItemNameId nameid;
nullpo_retz(sd);
- item = sd->inventory_data[n];
+ P<struct item_data> item = TRY_UNWRAP(sd->inventory_data[n], return 0);
nameid = sd->status.inventory[n].nameid;
- if (item == nullptr)
- return 0;
if (itemdb_type(nameid) != ItemType::USE)
return 0;
@@ -2223,7 +2154,9 @@ int pc_useitem(dumb_ptr<map_session_data> sd, IOff0 n)
nullpo_retr(1, sd);
- if (n.ok() && sd->inventory_data[n])
+ if (!n.ok())
+ return 0;
+ OMATCH_BEGIN_SOME (sdidn, sd->inventory_data[n])
{
amount = sd->status.inventory[n].amount;
if (!sd->status.inventory[n].nameid
@@ -2234,12 +2167,13 @@ int pc_useitem(dumb_ptr<map_session_data> sd, IOff0 n)
return 1;
}
- const ScriptBuffer *script = sd->inventory_data[n]->use_script.get();
+ P<const ScriptBuffer> script = borrow(*sdidn->use_script);
clif_useitemack(sd, n, amount - 1, 1);
pc_delitem(sd, n, 1, 1);
run_script(ScriptPointer(script, 0), sd->bl_id, BlockId());
}
+ OMATCH_END ();
return 0;
}
@@ -2277,18 +2211,19 @@ int pc_setpos(dumb_ptr<map_session_data> sd,
mapname_ = mapname_org;
- map_local *m = map_mapname2mapid(mapname_);
- if (!m)
+ Option<P<map_local>> m_ = map_mapname2mapid(mapname_);
+ if (m_.is_none())
{
if (sd->mapname_)
{
IP4Address ip;
int port;
- if (map_mapname2ipport(mapname_, &ip, &port) == 0)
+ if (map_mapname2ipport(mapname_, borrow(ip), borrow(port)) == 0)
{
skill_stop_dancing(sd, 1);
clif_clearchar(sd, clrtype);
map_delblock(sd);
+ // *cringe*
sd->mapname_ = mapname_;
sd->bl_x = x;
sd->bl_y = y;
@@ -2310,6 +2245,7 @@ int pc_setpos(dumb_ptr<map_session_data> sd,
#endif
return 1;
}
+ P<map_local> m = TRY_UNWRAP(m_, abort());
if (x < 0 || x >= m->xs || y < 0 || y >= m->ys)
x = y = 0;
@@ -2353,35 +2289,6 @@ int pc_setpos(dumb_ptr<map_session_data> sd,
}
/*==========================================
- * PCのランダムワープ
- *------------------------------------------
- */
-int pc_randomwarp(dumb_ptr<map_session_data> sd, BeingRemoveWhy type)
-{
- int x, y, i = 0;
-
- nullpo_retz(sd);
-
- map_local *m = sd->bl_m;
-
- if (sd->bl_m->flag.get(MapFlag::NOTELEPORT)) // テレポート禁止
- return 0;
-
- do
- {
- x = random_::in(1, m->xs - 2);
- y = random_::in(1, m->ys - 2);
- }
- while (bool(read_gatp(m, x, y) & MapCell::UNWALKABLE)
- && (i++) < 1000);
-
- if (i < 1000)
- pc_setpos(sd, m->name_, x, y, type);
-
- return 0;
-}
-
-/*==========================================
*
*------------------------------------------
*/
@@ -2504,8 +2411,8 @@ void pc_walk(TimerData *, tick_t tick, BlockId id, unsigned char data)
if (sd->status.party_id)
{ // パーティのHP情報通知検査
- PartyPair p = party_search(sd->status.party_id);
- if (p)
+ Option<PartyPair> p = party_search(sd->status.party_id);
+ if (p.is_some())
{
int p_flag = 0;
map_foreachinmovearea(std::bind(party_send_hp_check, ph::_1, sd->status.party_id, &p_flag),
@@ -2631,72 +2538,6 @@ void pc_touch_all_relevant_npcs(dumb_ptr<map_session_data> sd)
sd->areanpc_id = BlockId();
}
-/*==========================================
- *
- *------------------------------------------
- */
-int pc_movepos(dumb_ptr<map_session_data> sd, int dst_x, int dst_y)
-{
- int moveblock;
- int dx, dy;
-
- struct walkpath_data wpd;
-
- nullpo_retz(sd);
-
- if (path_search(&wpd, sd->bl_m, sd->bl_x, sd->bl_y, dst_x, dst_y, 0))
- return 1;
-
- sd->dir = sd->head_dir = map_calc_dir(sd, dst_x, dst_y);
-
- dx = dst_x - sd->bl_x;
- dy = dst_y - sd->bl_y;
-
- moveblock = (sd->bl_x / BLOCK_SIZE != dst_x / BLOCK_SIZE
- || sd->bl_y / BLOCK_SIZE != dst_y / BLOCK_SIZE);
-
- map_foreachinmovearea(std::bind(clif_pcoutsight, ph::_1, sd),
- sd->bl_m,
- sd->bl_x - AREA_SIZE, sd->bl_y - AREA_SIZE,
- sd->bl_x + AREA_SIZE, sd->bl_y + AREA_SIZE,
- dx, dy,
- BL::NUL);
-
- if (moveblock)
- map_delblock(sd);
- sd->bl_x = dst_x;
- sd->bl_y = dst_y;
- if (moveblock)
- map_addblock(sd);
-
- map_foreachinmovearea(std::bind(clif_pcinsight, ph::_1, sd),
- sd->bl_m,
- sd->bl_x - AREA_SIZE, sd->bl_y - AREA_SIZE,
- sd->bl_x + AREA_SIZE, sd->bl_y + AREA_SIZE,
- -dx, -dy,
- BL::NUL);
-
- if (sd->status.party_id)
- { // パーティのHP情報通知検査
- PartyPair p = party_search(sd->status.party_id);
- if (p)
- {
- int flag = 0;
- map_foreachinmovearea(std::bind(party_send_hp_check, ph::_1, sd->status.party_id, &flag),
- sd->bl_m,
- sd->bl_x - AREA_SIZE, sd->bl_y - AREA_SIZE,
- sd->bl_x + AREA_SIZE, sd->bl_y + AREA_SIZE,
- -dx, -dy,
- BL::PC);
- if (flag)
- sd->party_hp = -1;
- }
- }
-
- pc_touch_all_relevant_npcs(sd);
- return 0;
-}
-
//
// 武器戦闘
//
@@ -2764,8 +2605,8 @@ void pc_attack_timer(TimerData *, tick_t tick, BlockId id)
if (sd->opt1 != Opt1::ZERO)
return;
- Option *opt = battle_get_option(bl);
- if (opt != nullptr && bool(*opt & Option::REAL_ANY_HIDE))
+ Opt0 *opt = battle_get_option(bl);
+ if (opt != nullptr && bool(*opt & Opt0::REAL_ANY_HIDE))
return;
if (!battle_config.skill_delay_attack_enable)
@@ -2824,7 +2665,7 @@ void pc_attack_timer(TimerData *, tick_t tick, BlockId id)
sd->attackabletime = tick + (sd->aspd * 2);
}
if (sd->attackabletime <= tick)
- sd->attackabletime = tick + static_cast<interval_t>(battle_config.max_aspd) * 2;
+ sd->attackabletime = tick + battle_config.max_aspd * 2;
}
}
@@ -3017,6 +2858,12 @@ int pc_gainexp_reason(dumb_ptr<map_session_data> sd, int base_exp, int job_exp,
}
}
+ // Double Xp Weekends
+ base_exp = (base_exp * static_cast<double>(battle_config.base_exp_rate) / 100.);
+ if (base_exp <= 0)
+ base_exp = 0;
+ else if (base_exp > 1000000000)
+ base_exp = 1000000000;
sd->status.base_exp += base_exp;
// [Fate] Adjust experience points that healers can extract from this character
@@ -3024,7 +2871,6 @@ int pc_gainexp_reason(dumb_ptr<map_session_data> sd, int base_exp, int job_exp,
{
const int max_heal_xp =
20 + (sd->status.base_level * sd->status.base_level);
-
sd->heal_xp += base_exp;
if (sd->heal_xp > max_heal_xp)
sd->heal_xp = max_heal_xp;
@@ -3047,6 +2893,12 @@ int pc_gainexp_reason(dumb_ptr<map_session_data> sd, int base_exp, int job_exp,
}
}
+ // Double Xp Weekends
+ job_exp = (job_exp * static_cast<double>(battle_config.job_exp_rate) / 100.);
+ if (job_exp <= 0)
+ job_exp = 0;
+ else if (job_exp > 1000000000)
+ job_exp = 1000000000;
sd->status.job_exp += job_exp;
if (sd->status.job_exp < 0)
sd->status.job_exp = 0;
@@ -3247,94 +3099,6 @@ int pc_skillup(dumb_ptr<map_session_data> sd, SkillID skill_num)
}
/*==========================================
- * /resetlvl
- *------------------------------------------
- */
-int pc_resetlvl(dumb_ptr<map_session_data> sd, int type)
-{
- nullpo_retz(sd);
-
- for (SkillID i : erange(SkillID(1), MAX_SKILL))
- {
- sd->status.skill[i].lv = 0;
- }
-
- if (type == 1)
- {
- sd->status.skill_point = 0;
- sd->status.base_level = 1;
- sd->status.job_level = 1;
- sd->status.base_exp = 0;
- sd->status.job_exp = 0;
- sd->status.option = Option::ZERO;
-
- for (ATTR attr : ATTRs)
- sd->status.attrs[attr] = 1;
- }
-
- if (type == 2)
- {
- sd->status.skill_point = 0;
- sd->status.base_level = 1;
- sd->status.job_level = 1;
- sd->status.base_exp = 0;
- sd->status.job_exp = 0;
- }
- if (type == 3)
- {
- sd->status.base_level = 1;
- sd->status.base_exp = 0;
- }
- if (type == 4)
- {
- sd->status.job_level = 1;
- sd->status.job_exp = 0;
- }
-
- clif_updatestatus(sd, SP::STATUSPOINT);
- clif_updatestatus(sd, SP::STR);
- clif_updatestatus(sd, SP::AGI);
- clif_updatestatus(sd, SP::VIT);
- clif_updatestatus(sd, SP::INT);
- clif_updatestatus(sd, SP::DEX);
- clif_updatestatus(sd, SP::LUK);
- clif_updatestatus(sd, SP::BASELEVEL);
- clif_updatestatus(sd, SP::JOBLEVEL);
- clif_updatestatus(sd, SP::STATUSPOINT);
- clif_updatestatus(sd, SP::NEXTBASEEXP);
- clif_updatestatus(sd, SP::NEXTJOBEXP);
- clif_updatestatus(sd, SP::SKILLPOINT);
-
- clif_updatestatus(sd, SP::USTR); // Updates needed stat points - Valaris
- clif_updatestatus(sd, SP::UAGI);
- clif_updatestatus(sd, SP::UVIT);
- clif_updatestatus(sd, SP::UINT);
- clif_updatestatus(sd, SP::UDEX);
- clif_updatestatus(sd, SP::ULUK); // End Addition
-
- for (EQUIP i : EQUIPs)
- {
- // unequip items that can't be equipped by base 1 [Valaris]
- IOff0 *idx = &sd->equip_index_maybe[i];
- if ((*idx).ok())
- {
- if (!pc_isequip(sd, *idx))
- {
- pc_unequipitem(sd, *idx, CalcStatus::LATER);
- *idx = IOff0::from(-1);
- }
- }
- }
-
- clif_skillinfoblock(sd);
- pc_calcstatus(sd, 0);
-
- MAP_LOG_STATS(sd, "STATRESET"_fmt);
-
- return 0;
-}
-
-/*==========================================
* /resetstate
*------------------------------------------
*/
@@ -3435,9 +3199,12 @@ int pc_damage(dumb_ptr<block_list> src, dumb_ptr<map_session_data> sd,
if (sd->status.party_id)
{ // on-the-fly party hp updates [Valaris]
- PartyPair p = party_search(sd->status.party_id);
- if (p)
+ Option<PartyPair> p_ = party_search(sd->status.party_id);
+ OMATCH_BEGIN_SOME (p, p_)
+ {
clif_party_hp(p, sd);
+ }
+ OMATCH_END ();
} // end addition [Valaris]
return 0;
@@ -3728,8 +3495,7 @@ int pc_setparam(dumb_ptr<map_session_data> sd, SP type, int val)
}
break;
case SP::SEX:
- // this is a really bad idea
- sd->sex = static_cast<SEX>(val);
+ chrif_char_ask_name(AccountId(), sd->status_key.name, 5, HumanTimeDiff());
break;
case SP::WEIGHT:
sd->weight = val;
@@ -3755,7 +3521,7 @@ int pc_setparam(dumb_ptr<map_session_data> sd, SP type, int val)
case SP::INT:
case SP::DEX:
case SP::LUK:
- sd->status.attrs[sp_to_attr(type)] = val;
+ pc_statusup2(sd, type, (val - sd->status.attrs[sp_to_attr(type)]));
break;
}
clif_updatestatus(sd, type);
@@ -3803,9 +3569,12 @@ int pc_heal(dumb_ptr<map_session_data> sd, int hp, int sp)
if (sd->status.party_id)
{ // on-the-fly party hp updates [Valaris]
- PartyPair p = party_search(sd->status.party_id);
- if (p)
+ Option<PartyPair> p_ = party_search(sd->status.party_id);
+ OMATCH_BEGIN_SOME (p, p_)
+ {
clif_party_hp(p, sd);
+ }
+ OMATCH_END ();
} // end addition [Valaris]
return hp + sp;
@@ -3933,75 +3702,6 @@ int pc_itemheal_effect(dumb_ptr<map_session_data> sd, int hp, int sp)
}
/*==========================================
- * HP/SP回復
- *------------------------------------------
- */
-int pc_percentheal(dumb_ptr<map_session_data> sd, int hp, int sp)
-{
- nullpo_retz(sd);
-
- if (pc_checkoverhp(sd))
- {
- if (hp > 0)
- hp = 0;
- }
- if (pc_checkoversp(sd))
- {
- if (sp > 0)
- sp = 0;
- }
- if (hp)
- {
- if (hp >= 100)
- {
- sd->status.hp = sd->status.max_hp;
- }
- else if (hp <= -100)
- {
- sd->status.hp = 0;
- pc_damage(nullptr, sd, 1);
- }
- else
- {
- sd->status.hp += sd->status.max_hp * hp / 100;
- if (sd->status.hp > sd->status.max_hp)
- sd->status.hp = sd->status.max_hp;
- if (sd->status.hp <= 0)
- {
- sd->status.hp = 0;
- pc_damage(nullptr, sd, 1);
- hp = 0;
- }
- }
- }
- if (sp)
- {
- if (sp >= 100)
- {
- sd->status.sp = sd->status.max_sp;
- }
- else if (sp <= -100)
- {
- sd->status.sp = 0;
- }
- else
- {
- sd->status.sp += sd->status.max_sp * sp / 100;
- if (sd->status.sp > sd->status.max_sp)
- sd->status.sp = sd->status.max_sp;
- if (sd->status.sp < 0)
- sd->status.sp = 0;
- }
- }
- if (hp)
- clif_updatestatus(sd, SP::HP);
- if (sp)
- clif_updatestatus(sd, SP::SP);
-
- return 0;
-}
-
-/*==========================================
* 見た目変更
*------------------------------------------
*/
@@ -4044,21 +3744,6 @@ int pc_changelook(dumb_ptr<map_session_data> sd, LOOK type, int val)
}
/*==========================================
- * 付属品(鷹,ペコ,カート)設定
- *------------------------------------------
- */
-int pc_setoption(dumb_ptr<map_session_data> sd, Option type)
-{
- nullpo_retz(sd);
-
- sd->status.option = type;
- clif_changeoption(sd);
- pc_calcstatus(sd, 0);
-
- return 0;
-}
-
-/*==========================================
* script用変数の値を読む
*------------------------------------------
*/
@@ -4088,11 +3773,8 @@ ZString pc_readregstr(dumb_ptr<map_session_data> sd, SIR reg)
{
nullpo_retr(ZString(), sd);
- RString *s = sd->regstrm.search(reg);
- if (s)
- return *s;
-
- return ZString();
+ Option<P<RString>> s = sd->regstrm.search(reg);
+ return s.map([](P<RString> s_) -> ZString { return *s_; }).copy_or(""_s);
}
/*==========================================
@@ -4119,14 +3801,37 @@ void pc_setregstr(dumb_ptr<map_session_data> sd, SIR reg, RString str)
int pc_readglobalreg(dumb_ptr<map_session_data> sd, VarName reg)
{
int i;
-
+ int quest_shift = 0;
+ int quest_mask = 0;
nullpo_retz(sd);
+ QuestId questid;
+ XString var = reg;
+ VarName vr;
assert (sd->status.global_reg_num < GLOBAL_REG_NUM);
+ Option<P<struct quest_data>> quest_data_ = questdb_searchname(var);
+ OMATCH_BEGIN_SOME(quest_data, quest_data_)
+ {
+ questid = quest_data->questid;
+ reg = quest_data->quest_vr;
+ vr = quest_data->quest_var;
+ quest_shift = quest_data->quest_shift;
+ quest_mask = quest_data->quest_mask;
+ }
+ OMATCH_END ();
for (i = 0; i < sd->status.global_reg_num; i++)
{
if (sd->status.global_reg[i].str == reg)
- return sd->status.global_reg[i].value;
+ {
+ if (questid)
+ {
+ return ((sd->status.global_reg[i].value & (((1 << quest_mask) - 1) << (quest_shift * quest_mask))) >> (quest_shift * quest_mask));
+ }
+ else
+ {
+ return sd->status.global_reg[i].value;
+ }
+ }
}
return 0;
@@ -4139,8 +3844,13 @@ int pc_readglobalreg(dumb_ptr<map_session_data> sd, VarName reg)
int pc_setglobalreg(dumb_ptr<map_session_data> sd, VarName reg, int val)
{
int i;
-
+ int quest_shift = 0;
+ int quest_mask = 0;
+ int bitval = val;
nullpo_retz(sd);
+ QuestId questid;
+ XString var = reg;
+ VarName vr;
//PC_DIE_COUNTERがスクリプトなどで変更された時の処理
if (reg == stringish<VarName>("PC_DIE_COUNTER"_s) && sd->die_counter != val)
@@ -4148,6 +3858,17 @@ int pc_setglobalreg(dumb_ptr<map_session_data> sd, VarName reg, int val)
sd->die_counter = val;
pc_calcstatus(sd, 0);
}
+ Option<P<struct quest_data>> quest_data_ = questdb_searchname(var);
+ OMATCH_BEGIN_SOME(quest_data, quest_data_)
+ {
+ questid = quest_data->questid;
+ reg = quest_data->quest_vr;
+ vr = quest_data->quest_var;
+ quest_shift = quest_data->quest_shift;
+ quest_mask = quest_data->quest_mask;
+ assert (((1 << quest_mask) - 1) >= val);
+ }
+ OMATCH_END ();
assert (sd->status.global_reg_num < GLOBAL_REG_NUM);
if (val == 0)
{
@@ -4155,9 +3876,18 @@ int pc_setglobalreg(dumb_ptr<map_session_data> sd, VarName reg, int val)
{
if (sd->status.global_reg[i].str == reg)
{
- sd->status.global_reg[i] =
- sd->status.global_reg[sd->status.global_reg_num - 1];
- sd->status.global_reg_num--;
+ if (questid)
+ {
+ bitval = ((sd->status.global_reg[i].value & ~(((1 << quest_mask) - 1) << (quest_shift * quest_mask))) | (val << (quest_shift * quest_mask)));
+ clif_sendquest(sd, questid, val);
+ }
+ sd->status.global_reg[i].value = bitval;
+ if (sd->status.global_reg[i].value == 0)
+ {
+ sd->status.global_reg[i] =
+ sd->status.global_reg[sd->status.global_reg_num - 1];
+ sd->status.global_reg_num--;
+ }
break;
}
}
@@ -4167,14 +3897,24 @@ int pc_setglobalreg(dumb_ptr<map_session_data> sd, VarName reg, int val)
{
if (sd->status.global_reg[i].str == reg)
{
- sd->status.global_reg[i].value = val;
+ if (questid)
+ {
+ bitval = ((sd->status.global_reg[i].value & ~(((1 << quest_mask) - 1) << (quest_shift * quest_mask))) | (val << (quest_shift * quest_mask)));
+ clif_sendquest(sd, questid, val);
+ }
+ sd->status.global_reg[i].value = bitval;
return 0;
}
}
if (sd->status.global_reg_num < GLOBAL_REG_NUM)
{
sd->status.global_reg[i].str = reg;
- sd->status.global_reg[i].value = val;
+ if (questid)
+ {
+ bitval = ((sd->status.global_reg[i].value & ~(((1 << quest_mask) - 1) << (quest_shift * quest_mask))) | (val << (quest_shift * quest_mask)));
+ clif_sendquest(sd, questid, val);
+ }
+ sd->status.global_reg[i].value = bitval;
sd->status.global_reg_num++;
return 0;
}
@@ -4400,7 +4140,6 @@ int pc_signal_advanced_equipment_change(dumb_ptr<map_session_data> sd, IOff0 n)
int pc_equipitem(dumb_ptr<map_session_data> sd, IOff0 n, EPOS)
{
ItemNameId nameid;
- struct item_data *id;
//ソス]ソスソスソスソスソス{ソスqソスフ場合ソスフ鯉ソスソスフ職ソスニゑソスソスZソスoソスソスソスソス
nullpo_retz(sd);
@@ -4412,9 +4151,8 @@ int pc_equipitem(dumb_ptr<map_session_data> sd, IOff0 n, EPOS)
}
nameid = sd->status.inventory[n].nameid;
- id = sd->inventory_data[n];
- if (!id) // can't actually happen - the only caller checks this.
- return 0;
+ // can't actually happen - the only caller checks this.
+ P<struct item_data> id = TRY_UNWRAP(sd->inventory_data[n], return 0);
EPOS pos = pc_equippoint(sd, n);
if (battle_config.battle_log)
@@ -4475,17 +4213,18 @@ int pc_equipitem(dumb_ptr<map_session_data> sd, IOff0 n, EPOS)
ItemNameId view_i;
ItemLook view_l = ItemLook::NONE;
// TODO: This is ugly.
- if (sd->inventory_data[n])
+ OMATCH_BEGIN_SOME (sdidn, sd->inventory_data[n])
{
- bool look_not_weapon = sd->inventory_data[n]->look == ItemLook::NONE;
+ bool look_not_weapon = sdidn->look == ItemLook::NONE;
bool equip_is_weapon = bool(sd->status.inventory[n].equip & EPOS::WEAPON);
assert (look_not_weapon != equip_is_weapon);
if (look_not_weapon)
- view_i = sd->inventory_data[n]->nameid;
+ view_i = sdidn->nameid;
else
- view_l = sd->inventory_data[n]->look;
+ view_l = sdidn->look;
}
+ OMATCH_END ();
if (bool(sd->status.inventory[n].equip & EPOS::WEAPON))
{
@@ -4495,25 +4234,27 @@ int pc_equipitem(dumb_ptr<map_session_data> sd, IOff0 n, EPOS)
}
if (bool(sd->status.inventory[n].equip & EPOS::SHIELD))
{
- if (sd->inventory_data[n])
+ OMATCH_BEGIN (sd->inventory_data[n])
{
- if (sd->inventory_data[n]->type == ItemType::WEAPON)
+ OMATCH_CASE_SOME (sdidn)
{
- sd->status.shield = ItemNameId();
- if (sd->status.inventory[n].equip == EPOS::SHIELD)
- sd->weapontype2 = view_l;
+ if (sdidn->type == ItemType::WEAPON)
+ {
+ sd->status.shield = ItemNameId();
+ if (sd->status.inventory[n].equip == EPOS::SHIELD)
+ assert(0 && "unreachable - offhand weapons are not supported");
+ }
+ else if (sdidn->type == ItemType::ARMOR)
+ {
+ sd->status.shield = view_i;
+ }
}
- else if (sd->inventory_data[n]->type == ItemType::ARMOR)
+ OMATCH_CASE_NONE ()
{
- sd->status.shield = view_i;
- sd->weapontype2 = ItemLook::NONE;
+ sd->status.shield = ItemNameId();
}
}
- else
- {
- sd->status.shield = ItemNameId();
- sd->weapontype2 = ItemLook::NONE;
- }
+ OMATCH_END ();
pc_calcweapontype(sd);
clif_changelook(sd, LOOK::SHIELD, unwrap<ItemNameId>(sd->status.shield));
}
@@ -4563,14 +4304,14 @@ int pc_unequipitem(dumb_ptr<map_session_data> sd, IOff0 n, CalcStatus type)
if (bool(sd->status.inventory[n].equip & EPOS::WEAPON))
{
sd->weapontype1 = ItemLook::NONE;
- sd->status.weapon = sd->weapontype2;
+ // when reading the diff, think twice about this
+ sd->status.weapon = ItemLook::NONE;
pc_calcweapontype(sd);
pc_set_weapon_look(sd);
}
if (bool(sd->status.inventory[n].equip & EPOS::SHIELD))
{
sd->status.shield = ItemNameId();
- sd->weapontype2 = ItemLook::NONE;
pc_calcweapontype(sd);
clif_changelook(sd, LOOK::SHIELD, unwrap<ItemNameId>(sd->status.shield));
}
@@ -4728,8 +4469,7 @@ void pc_calc_pvprank_sub(dumb_ptr<block_list> bl, dumb_ptr<map_session_data> sd2
int pc_calc_pvprank(dumb_ptr<map_session_data> sd)
{
nullpo_retz(sd);
- map_local *m = sd->bl_m;
- nullpo_retz(m);
+ P<map_local> m = sd->bl_m;
if (!(m->flag.get(MapFlag::PVP)))
return 0;
@@ -4816,15 +4556,12 @@ int pc_divorce(dumb_ptr<map_session_data> sd)
}
p_sd->status.partner_id = CharId();
sd->status.partner_id = CharId();
-
- if (sd->npc_flags.divorce)
- {
- sd->npc_flags.divorce = 0;
- map_scriptcont(sd, sd->npc_id);
- }
}
else
+ {
+ sd->status.partner_id = CharId();
chrif_send_divorce(sd->status_key.char_id);
+ }
return 0;
}
@@ -4857,10 +4594,6 @@ dumb_ptr<map_session_data> pc_get_partner(dumb_ptr<map_session_data> sd)
* SP回復量計算
*------------------------------------------
*/
-static
-tick_t natural_heal_tick, natural_heal_prev_tick;
-static
-interval_t natural_heal_diff_tick;
static
interval_t pc_spheal(dumb_ptr<map_session_data> sd)
@@ -4918,12 +4651,12 @@ int pc_natural_heal_hp(dumb_ptr<map_session_data> sd)
return 0;
}
- if (sd->hp_sub >= static_cast<interval_t>(battle_config.natural_healhp_interval))
+ if (sd->hp_sub >= battle_config.natural_healhp_interval)
{
bonus = sd->nhealhp;
- while (sd->hp_sub >= static_cast<interval_t>(battle_config.natural_healhp_interval))
+ while (sd->hp_sub >= battle_config.natural_healhp_interval)
{
- sd->hp_sub -= static_cast<interval_t>(battle_config.natural_healhp_interval);
+ sd->hp_sub -= battle_config.natural_healhp_interval;
if (sd->status.hp + bonus <= sd->status.max_hp)
sd->status.hp += bonus;
else
@@ -4936,28 +4669,7 @@ int pc_natural_heal_hp(dumb_ptr<map_session_data> sd)
if (bhp != sd->status.hp)
clif_updatestatus(sd, SP::HP);
- if (sd->nshealhp > 0)
- {
- if (sd->inchealhptick >= static_cast<interval_t>(battle_config.natural_heal_skill_interval)
- && sd->status.hp < sd->status.max_hp)
- {
- bonus = sd->nshealhp;
- while (sd->inchealhptick >= static_cast<interval_t>(battle_config.natural_heal_skill_interval))
- {
- sd->inchealhptick -= static_cast<interval_t>(battle_config.natural_heal_skill_interval);
- if (sd->status.hp + bonus <= sd->status.max_hp)
- sd->status.hp += bonus;
- else
- {
- bonus = sd->status.max_hp - sd->status.hp;
- sd->status.hp = sd->status.max_hp;
- sd->hp_sub = sd->inchealhptick = interval_t::zero();
- }
- }
- }
- }
- else
- sd->inchealhptick = interval_t::zero();
+ sd->inchealhptick = interval_t::zero();
return 0;
}
@@ -4985,12 +4697,12 @@ int pc_natural_heal_sp(dumb_ptr<map_session_data> sd)
else
sd->inchealsptick = interval_t::zero();
- if (sd->sp_sub >= static_cast<interval_t>(battle_config.natural_healsp_interval))
+ if (sd->sp_sub >= battle_config.natural_healsp_interval)
{
bonus = sd->nhealsp;
- while (sd->sp_sub >= static_cast<interval_t>(battle_config.natural_healsp_interval))
+ while (sd->sp_sub >= battle_config.natural_healsp_interval)
{
- sd->sp_sub -= static_cast<interval_t>(battle_config.natural_healsp_interval);
+ sd->sp_sub -= battle_config.natural_healsp_interval;
if (sd->status.sp + bonus <= sd->status.max_sp)
sd->status.sp += bonus;
else
@@ -5004,28 +4716,7 @@ int pc_natural_heal_sp(dumb_ptr<map_session_data> sd)
if (bsp != sd->status.sp)
clif_updatestatus(sd, SP::SP);
- if (sd->nshealsp > 0)
- {
- if (sd->inchealsptick >= static_cast<interval_t>(battle_config.natural_heal_skill_interval)
- && sd->status.sp < sd->status.max_sp)
- {
- bonus = sd->nshealsp;
- while (sd->inchealsptick >= static_cast<interval_t>(battle_config.natural_heal_skill_interval))
- {
- sd->inchealsptick -= static_cast<interval_t>(battle_config.natural_heal_skill_interval);
- if (sd->status.sp + bonus <= sd->status.max_sp)
- sd->status.sp += bonus;
- else
- {
- bonus = sd->status.max_sp - sd->status.sp;
- sd->status.sp = sd->status.max_sp;
- sd->sp_sub = sd->inchealsptick = interval_t::zero();
- }
- }
- }
- }
- else
- sd->inchealsptick = interval_t::zero();
+ sd->inchealsptick = interval_t::zero();
return 0;
}
@@ -5147,8 +4838,6 @@ void pc_setsavepoint(dumb_ptr<map_session_data> sd, MapName mapname, int x, int
*------------------------------------------
*/
static
-int last_save_fd, save_flag;
-static
void pc_autosave_sub(dumb_ptr<map_session_data> sd)
{
nullpo_retv(sd);
@@ -5175,7 +4864,7 @@ void pc_autosave(TimerData *, tick_t)
if (save_flag == 0)
last_save_fd = -1;
- interval_t interval = autosave_time / (clif_countusers() + 1);
+ interval_t interval = map_conf.autosave_time / (clif_countusers() + 1);
if (interval <= interval_t::zero())
interval = 1_ms;
Timer(gettick() + interval,
@@ -5228,7 +4917,7 @@ void do_init_pc(void)
pc_natural_heal,
NATURAL_HEAL_INTERVAL
).detach();
- Timer(gettick() + autosave_time,
+ Timer(gettick() + map_conf.autosave_time,
pc_autosave
).detach();
}
@@ -5240,15 +4929,15 @@ void pc_cleanup(dumb_ptr<map_session_data> sd)
void pc_invisibility(dumb_ptr<map_session_data> sd, int enabled)
{
- if (enabled && !bool(sd->status.option & Option::INVISIBILITY))
+ if (enabled && !bool(sd->status.option & Opt0::INVISIBILITY))
{
clif_clearchar(sd, BeingRemoveWhy::WARPED);
- sd->status.option |= Option::INVISIBILITY;
+ sd->status.option |= Opt0::INVISIBILITY;
clif_status_change(sd, StatusChange::CLIF_OPTION_SC_INVISIBILITY, 1);
}
else if (!enabled)
{
- sd->status.option &= ~Option::INVISIBILITY;
+ sd->status.option &= ~Opt0::INVISIBILITY;
clif_status_change(sd, StatusChange::CLIF_OPTION_SC_INVISIBILITY, 0);
pc_setpos(sd, sd->bl_m->name_, sd->bl_x, sd->bl_y, BeingRemoveWhy::WARPED);
}
@@ -5280,4 +4969,5 @@ int pc_logout(dumb_ptr<map_session_data> sd) // [fate] Player logs out
MAP_LOG_STATS(sd, "LOGOUT"_fmt);
return 0;
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/pc.hpp b/src/map/pc.hpp
index 3187cd9..d100938 100644
--- a/src/map/pc.hpp
+++ b/src/map/pc.hpp
@@ -20,24 +20,21 @@
// 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 "fwd.hpp"
-
#include "pc.t.hpp"
-#include "../strings/fwd.hpp"
+#include "fwd.hpp"
#include "../generic/dumb_ptr.hpp"
-#include "../mmo/utils.hpp"
-
-#include "../proto2/fwd.hpp"
-
-#include "clif.t.hpp"
+#include "../mmo/clif.t.hpp"
#include "map.hpp"
+#include "quest.hpp"
namespace tmwa
{
+namespace map
+{
inline
void pc_setsit(dumb_ptr<map_session_data> sd)
{
@@ -62,7 +59,7 @@ void pc_setdir(dumb_ptr<map_session_data> sd, DIR b)
inline
bool pc_isinvisible(dumb_ptr<map_session_data> sd)
{
- return bool(sd->status.option & Option::HIDE);
+ return bool(sd->status.option & Opt0::HIDE);
}
inline
bool pc_is90overweight(dumb_ptr<map_session_data> sd)
@@ -83,7 +80,7 @@ int pc_counttargeted(dumb_ptr<map_session_data> sd, dumb_ptr<block_list> src,
int pc_setrestartvalue(dumb_ptr<map_session_data> sd, int type);
void pc_makesavestatus(dumb_ptr<map_session_data>);
int pc_setnewpc(dumb_ptr<map_session_data>, AccountId, CharId, int, uint32_t /*tick_t*/, SEX);
-int pc_authok(AccountId, int, TimeT, short tmw_version, const CharKey *, const CharData *);
+int pc_authok(AccountId, int, short tmw_version, const CharKey *, const CharData *);
int pc_authfail(AccountId accid);
EPOS pc_equippoint(dumb_ptr<map_session_data> sd, IOff0 n);
@@ -93,10 +90,8 @@ IOff0 pc_checkequip(dumb_ptr<map_session_data> sd, EPOS pos);
int pc_walktoxy(dumb_ptr<map_session_data>, int, int);
int pc_stop_walking(dumb_ptr<map_session_data>, int);
-int pc_movepos(dumb_ptr<map_session_data>, int, int);
int pc_setpos(dumb_ptr<map_session_data>, MapName, int, int, BeingRemoveWhy);
void pc_setsavepoint(dumb_ptr<map_session_data>, MapName, int, int);
-int pc_randomwarp(dumb_ptr<map_session_data> sd, BeingRemoveWhy type);
ADDITEM pc_checkadditem(dumb_ptr<map_session_data>, ItemNameId, int);
int pc_inventoryblank(dumb_ptr<map_session_data>);
@@ -133,7 +128,6 @@ int pc_need_status_point(dumb_ptr<map_session_data>, SP);
int pc_statusup(dumb_ptr<map_session_data>, SP);
int pc_statusup2(dumb_ptr<map_session_data>, SP, int);
int pc_skillup(dumb_ptr<map_session_data>, SkillID);
-int pc_resetlvl(dumb_ptr<map_session_data>, int type);
int pc_resetstate(dumb_ptr<map_session_data>);
int pc_resetskill(dumb_ptr<map_session_data>);
int pc_equipitem(dumb_ptr<map_session_data>, IOff0, EPOS);
@@ -144,8 +138,6 @@ int pc_useitem(dumb_ptr<map_session_data>, IOff0);
int pc_damage(dumb_ptr<block_list>, dumb_ptr<map_session_data>, int);
int pc_heal(dumb_ptr<map_session_data>, int, int);
int pc_itemheal(dumb_ptr<map_session_data> sd, int hp, int sp);
-int pc_percentheal(dumb_ptr<map_session_data> sd, int, int);
-int pc_setoption(dumb_ptr<map_session_data>, Option);
int pc_changelook(dumb_ptr<map_session_data>, LOOK, int);
int pc_readparam(dumb_ptr<map_session_data>, SP);
@@ -154,6 +146,8 @@ int pc_readreg(dumb_ptr<map_session_data>, SIR);
void pc_setreg(dumb_ptr<map_session_data>, SIR, int);
ZString pc_readregstr(dumb_ptr<map_session_data> sd, SIR reg);
void pc_setregstr(dumb_ptr<map_session_data> sd, SIR reg, RString str);
+void update_quest(dumb_ptr<map_session_data> sd, VarName quest_var, int value);
+void update_allquest(dumb_ptr<map_session_data> sd);
int pc_readglobalreg(dumb_ptr<map_session_data>, VarName );
int pc_setglobalreg(dumb_ptr<map_session_data>, VarName , int);
int pc_readaccountreg(dumb_ptr<map_session_data>, VarName );
@@ -177,6 +171,8 @@ void pc_setstand(dumb_ptr<map_session_data> sd);
void pc_cleanup(dumb_ptr<map_session_data> sd); // [Fate] Clean up after a logged-out PC
int pc_read_gm_account(Session *, const std::vector<Packet_Repeat<0x2b15>>&);
+int pc_setpvptimer(dumb_ptr<map_session_data> sd, interval_t);
+int pc_delpvptimer(dumb_ptr<map_session_data> sd);
int pc_setinvincibletimer(dumb_ptr<map_session_data> sd, interval_t);
int pc_delinvincibletimer(dumb_ptr<map_session_data> sd);
int pc_logout(dumb_ptr<map_session_data> sd); // [fate] Player logs out
@@ -184,4 +180,5 @@ int pc_logout(dumb_ptr<map_session_data> sd); // [fate] Player logs out
void pc_show_motd(dumb_ptr<map_session_data> sd);
void do_init_pc(void);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/pc.t.hpp b/src/map/pc.t.hpp
index 427e8c3..c9235fa 100644
--- a/src/map/pc.t.hpp
+++ b/src/map/pc.t.hpp
@@ -28,6 +28,8 @@
namespace tmwa
{
+namespace map
+{
enum class PC_GAINEXP_REASON
{
KILLING = 0,
@@ -54,4 +56,5 @@ enum class CalcStatus
NOW,
LATER,
};
+} // namespace map
} // namespace tmwa
diff --git a/src/map/quest.cpp b/src/map/quest.cpp
new file mode 100644
index 0000000..dfe19ff
--- /dev/null
+++ b/src/map/quest.cpp
@@ -0,0 +1,135 @@
+#include "quest.hpp"
+// quest.cpp - Quest Log.
+//
+// Copyright © 2015 Ed Pasek <pasekei@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 <algorithm>
+
+#include "../strings/astring.hpp"
+#include "../strings/zstring.hpp"
+#include "../strings/xstring.hpp"
+
+#include "../generic/db.hpp"
+
+#include "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
+#include "../io/line.hpp"
+
+#include "../mmo/config_parse.hpp"
+#include "../mmo/extract_enums.hpp"
+
+#include "../ast/quest.hpp"
+
+#include "../poison.hpp"
+#include "globals.hpp"
+#include "script-parse.hpp"
+
+namespace tmwa
+{
+namespace map
+{
+// Function declarations
+
+static
+void questdb_searchname_sub(Borrowed<struct quest_data> quest, VarName str, Borrowed<Option<Borrowed<struct quest_data>>> dst)
+{
+ if (quest->quest_var == str)
+ *dst = Some(quest);
+}
+
+Option<Borrowed<struct quest_data>> questdb_searchname(XString str_)
+{
+ VarName str = stringish<VarName>(str_);
+ if (XString(str) != str_)
+ return None;
+ Option<P<struct quest_data>> quest = None;
+ for (auto& pair : quest_db)
+ questdb_searchname_sub(borrow(pair.second), str, borrow(quest));
+ return quest;
+}
+
+Borrowed<struct quest_data> questdb_search(QuestId questid)
+{
+ Option<P<struct quest_data>> id_ = quest_db.search(questid);
+ OMATCH_BEGIN_SOME (id, id_)
+ {
+ return id;
+ }
+ OMATCH_END ();
+
+ P<struct quest_data> id = quest_db.init(questid);
+
+ id->questid = questid;
+
+ return id;
+}
+
+Option<Borrowed<struct quest_data>> questdb_exists(QuestId questid)
+{
+ return quest_db.search(questid);
+}
+
+bool quest_readdb(ZString filename)
+{
+ io::LineCharReader in(filename);
+
+ if (!in.is_open())
+ {
+ PRINTF("can't read %s\n"_fmt, filename);
+ return false;
+ }
+
+ int ln = 0;
+
+ while (true)
+ {
+ auto res = TRY_UNWRAP(ast::quest::parse_quest(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::quest::QuestOrComment ioc = TRY_UNWRAP(std::move(res.get_success()), return false);
+
+ MATCH_BEGIN (ioc)
+ {
+ MATCH_CASE (const ast::quest::Comment&, c)
+ {
+ (void)c;
+ }
+ MATCH_CASE (const ast::quest::Quest&, quest)
+ {
+ ln++;
+
+ quest_data qdv {};
+ qdv.questid = quest.questid.data;
+ qdv.quest_var = quest.quest_var.data;
+ qdv.quest_vr = quest.quest_vr.data;
+ qdv.quest_shift = quest.quest_shift.data;
+ qdv.quest_mask = quest.quest_mask.data;
+
+ Borrowed<struct quest_data> id = questdb_search(qdv.questid);
+ *id = std::move(qdv);
+ }
+ }
+ MATCH_END ();
+ }
+}
+} // namespace map
+} // namespace tmwa
diff --git a/src/sexpr/bind.cpp b/src/map/quest.hpp
index d8d0caa..65e6f4e 100644
--- a/src/sexpr/bind.cpp
+++ b/src/map/quest.hpp
@@ -1,7 +1,7 @@
-#include "bind.hpp"
-// bind.cpp - Just include the header file.
+#pragma once
+// quest.hpp - Quest Log.
//
-// Copyright © 2012 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2015 Ed Pasek <pasekei@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -18,12 +18,34 @@
// 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 "../poison.hpp"
+#include "fwd.hpp"
+#include "../mmo/ids.hpp"
+#include "../high/mmo.hpp"
+
+#include "map.hpp"
+#include "script-buffer.hpp"
namespace tmwa
{
-namespace sexpr
+namespace map
+{
+constexpr int MAX_QUEST_DB (60355+1);
+struct quest_data
{
-} // namespace sexpr
+ QuestId questid;
+ VarName quest_var;
+ VarName quest_vr;
+ int quest_shift;
+ int quest_mask;
+};
+inline
+Option<Borrowed<struct quest_data>> questdb_searchname(VarName) = delete;
+Option<Borrowed<struct quest_data>> questdb_searchname(XString quest_var);
+Borrowed<struct quest_data> questdb_search(QuestId questid);
+Option<Borrowed<struct quest_data>> questdb_exists(QuestId questid);
+
+// get quest var by quest name / mask / bit
+bool quest_readdb(ZString filename);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/script-buffer.hpp b/src/map/script-buffer.hpp
new file mode 100644
index 0000000..e720044
--- /dev/null
+++ b/src/map/script-buffer.hpp
@@ -0,0 +1,45 @@
+#pragma once
+// script-buffer.hpp - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+#include <memory>
+
+
+namespace tmwa
+{
+namespace map
+{
+class ScriptBuffer;
+} // namespace map
+} // namespace tmwa
+
+namespace std
+{
+template<>
+struct default_delete<const tmwa::map::ScriptBuffer>
+{
+ default_delete() {}
+ default_delete(default_delete<tmwa::map::ScriptBuffer>) {}
+ void operator()(const tmwa::map::ScriptBuffer *sd);
+};
+} // namespace std
diff --git a/src/map/script-call-internal.hpp b/src/map/script-call-internal.hpp
new file mode 100644
index 0000000..b9b3a9f
--- /dev/null
+++ b/src/map/script-call-internal.hpp
@@ -0,0 +1,100 @@
+#pragma once
+// script-call-internal.hpp - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "script-call.hpp"
+#include "fwd.hpp"
+
+#include "../mmo/ids.hpp"
+
+#include "script-persist.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+enum class VariableCode : uint8_t
+{
+ PARAM,
+ VARIABLE,
+};
+
+struct script_stack
+{
+ std::vector<struct script_data> stack_datav;
+};
+
+enum class ScriptEndState;
+// future improvements coming!
+class ScriptState
+{
+public:
+ struct script_stack *stack;
+ int start, end;
+ ScriptEndState state;
+ BlockId rid, oid;
+ ScriptPointer scriptp, new_scriptp;
+ int defsp, new_defsp, freeloop;
+};
+
+void run_func(ScriptState *st);
+
+enum class ScriptEndState
+{
+ ZERO,
+ STOP,
+ END,
+ RERUNLINE,
+ GOTO,
+ RETFUNC,
+};
+
+dumb_ptr<map_session_data> script_rid2sd(ScriptState *st);
+void get_val(dumb_ptr<map_session_data> sd, struct script_data *data);
+__attribute__((deprecated))
+void get_val(ScriptState *st, struct script_data *data);
+struct script_data get_val2(ScriptState *st, SIR reg);
+void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, struct script_data vd);
+void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, int id);
+void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, RString zd);
+__attribute__((warn_unused_result))
+RString conv_str(ScriptState *st, struct script_data *data);
+__attribute__((warn_unused_result))
+int conv_num(ScriptState *st, struct script_data *data);
+__attribute__((warn_unused_result))
+Borrowed<const ScriptBuffer> conv_script(ScriptState *st, struct script_data *data);
+
+template<class T>
+void push_int(struct script_stack *stack, int val);
+template<class T>
+void push_reg(struct script_stack *stack, SIR reg);
+template<class T>
+void push_script(struct script_stack *stack, Borrowed<const ScriptBuffer> code);
+template<class T>
+void push_str(struct script_stack *stack, RString str);
+
+void push_copy(struct script_stack *stack, int pos_);
+void pop_stack(struct script_stack *stack, int start, int end);
+} // namespace map
+} // namespace tmwa
+
+#include "script-call-internal.tcc"
diff --git a/src/map/script-call-internal.tcc b/src/map/script-call-internal.tcc
new file mode 100644
index 0000000..e10b69c
--- /dev/null
+++ b/src/map/script-call-internal.tcc
@@ -0,0 +1,79 @@
+// script-call-internal.tcc - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "script-persist.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+template<class D>
+bool first_type_is_any()
+{
+ return false;
+}
+
+template<class D, class F, class... R>
+constexpr
+bool first_type_is_any()
+{
+ return std::is_same<D, F>::value || first_type_is_any<D, R...>();
+}
+
+
+template<class T>
+void push_int(struct script_stack *stack, int val)
+{
+ static_assert(first_type_is_any<T, ScriptDataPos, ScriptDataInt, ScriptDataArg, ScriptDataFuncRef>(), "not int type");
+
+ script_data nsd = T{.numi= val};
+ stack->stack_datav.push_back(nsd);
+}
+
+template<class T>
+void push_reg(struct script_stack *stack, SIR reg)
+{
+ static_assert(first_type_is_any<T, ScriptDataParam, ScriptDataVariable>(), "not reg type");
+
+ script_data nsd = T{.reg= reg};
+ stack->stack_datav.push_back(nsd);
+}
+
+template<class T>
+void push_script(struct script_stack *stack, Borrowed<const ScriptBuffer> code)
+{
+ static_assert(first_type_is_any<T, ScriptDataRetInfo>(), "not scriptbuf type");
+
+ script_data nsd = T{.script= code};
+ stack->stack_datav.push_back(nsd);
+}
+
+template<class T>
+void push_str(struct script_stack *stack, RString str)
+{
+ static_assert(first_type_is_any<T, ScriptDataStr>(), "not str type");
+
+ script_data nsd = T{.str= str};
+ stack->stack_datav.push_back(nsd);
+}
+} // namespace map
+} // namespace tmwa
diff --git a/src/map/script-call.cpp b/src/map/script-call.cpp
new file mode 100644
index 0000000..c3c6aa1
--- /dev/null
+++ b/src/map/script-call.cpp
@@ -0,0 +1,926 @@
+#include "script-call-internal.hpp"
+// script-call.cpp - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011 Chuck Miller
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2013 wushin
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "../generic/intern-pool.hpp"
+
+#include "../io/cxxstdio.hpp"
+
+#include "../mmo/cxxstdio_enums.hpp"
+
+#include "battle.hpp"
+#include "battle_conf.hpp"
+#include "globals.hpp"
+#include "map.hpp"
+#include "npc.hpp"
+#include "pc.hpp"
+#include "script-fun.hpp"
+#include "script-parse-internal.hpp"
+#include "script-persist.hpp"
+#include "script-startup-internal.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+constexpr bool DEBUG_RUN = false;
+
+static
+struct ScriptConfigRun
+{
+ static const
+ int check_cmdcount = 8192;
+ static const
+ int check_gotocount = 512;
+} script_config;
+
+
+/*==========================================
+ * ridからsdへの解決
+ *------------------------------------------
+ */
+dumb_ptr<map_session_data> script_rid2sd(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = map_id2sd(st->rid);
+ if (!sd)
+ {
+ PRINTF("script_rid2sd: fatal error ! player not attached!\n"_fmt);
+ }
+ return sd;
+}
+
+/*==========================================
+ * 変数の読み取り
+ *------------------------------------------
+ */
+void get_val(dumb_ptr<map_session_data> sd, struct script_data *data)
+{
+ MATCH_BEGIN (*data)
+ {
+ MATCH_CASE (const ScriptDataParam&, u)
+ {
+ if (sd == nullptr)
+ PRINTF("get_val error param SP::%d\n"_fmt, u.reg.sp());
+ int numi = 0;
+ if (sd)
+ numi = pc_readparam(sd, u.reg.sp());
+ *data = ScriptDataInt{numi};
+ }
+ MATCH_CASE (const ScriptDataVariable&, u)
+ {
+ ZString name_ = variable_names.outtern(u.reg.base());
+ VarName name = stringish<VarName>(name_);
+ char prefix = name.front();
+ char postfix = name.back();
+
+ if (prefix != '$')
+ {
+ if (sd == nullptr)
+ PRINTF("get_val error name?:%s\n"_fmt, name);
+ }
+ if (postfix == '$')
+ {
+ RString str;
+ if (prefix == '@')
+ {
+ if (sd)
+ str = pc_readregstr(sd, u.reg);
+ }
+ else if (prefix == '$')
+ {
+ Option<P<RString>> s_ = mapregstr_db.search(u.reg);
+ OMATCH_BEGIN_SOME (s, s_)
+ {
+ str = *s;
+ }
+ OMATCH_END ();
+ }
+ else
+ {
+ PRINTF("script: get_val: illegal scope string variable.\n"_fmt);
+ str = "!!ERROR!!"_s;
+ }
+ *data = ScriptDataStr{str};
+ }
+ else
+ {
+ int numi = 0;
+ if (prefix == '@')
+ {
+ if (sd)
+ numi = pc_readreg(sd, u.reg);
+ }
+ else if (prefix == '$')
+ {
+ numi = mapreg_db.get(u.reg);
+ }
+ else if (prefix == '#')
+ {
+ if (name[1] == '#')
+ {
+ if (sd)
+ numi = pc_readaccountreg2(sd, name);
+ }
+ else
+ {
+ if (sd)
+ numi = pc_readaccountreg(sd, name);
+ }
+ }
+ else
+ {
+ if (sd)
+ numi = pc_readglobalreg(sd, name);
+ }
+ *data = ScriptDataInt{numi};
+ }
+ }
+ }
+ MATCH_END ();
+}
+
+void get_val(ScriptState *st, struct script_data *data)
+{
+ dumb_ptr<map_session_data> sd = st->rid ? map_id2sd(st->rid) : nullptr;
+ get_val(sd, data);
+}
+
+/*==========================================
+ * 変数の読み取り2
+ *------------------------------------------
+ */
+struct script_data get_val2(ScriptState *st, SIR reg)
+{
+ struct script_data dat = ScriptDataVariable{reg};
+ get_val(st, &dat);
+ return dat;
+}
+
+/*==========================================
+ * 変数設定用
+ *------------------------------------------
+ */
+void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, struct script_data vd)
+{
+ if (type == VariableCode::PARAM)
+ {
+ int val = vd.get_if<ScriptDataInt>()->numi;
+ pc_setparam(sd, reg.sp(), val);
+ return;
+ }
+ assert (type == VariableCode::VARIABLE);
+
+ ZString name_ = variable_names.outtern(reg.base());
+ VarName name = stringish<VarName>(name_);
+ char prefix = name.front();
+ char postfix = name.back();
+
+ if (postfix == '$')
+ {
+ RString str = vd.get_if<ScriptDataStr>()->str;
+ if (prefix == '@')
+ {
+ pc_setregstr(sd, reg, str);
+ }
+ else if (prefix == '$')
+ {
+ mapreg_setregstr(reg, str);
+ }
+ else
+ {
+ PRINTF("script: set_reg: illegal scope string variable !"_fmt);
+ }
+ }
+ else
+ {
+ int val = vd.get_if<ScriptDataInt>()->numi;
+ if (prefix == '@')
+ {
+ pc_setreg(sd, reg, val);
+ }
+ else if (prefix == '$')
+ {
+ mapreg_setreg(reg, val);
+ }
+ else if (prefix == '#')
+ {
+ if (name[1] == '#')
+ pc_setaccountreg2(sd, name, val);
+ else
+ pc_setaccountreg(sd, name, val);
+ }
+ else
+ {
+ pc_setglobalreg(sd, name, val);
+ }
+ }
+}
+
+void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, int id)
+{
+ struct script_data vd = ScriptDataInt{id};
+ set_reg(sd, type, reg, vd);
+}
+
+void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, RString zd)
+{
+ struct script_data vd = ScriptDataStr{zd};
+ set_reg(sd, type, reg, vd);
+}
+
+/*==========================================
+ * 文字列への変換
+ *------------------------------------------
+ */
+RString conv_str(ScriptState *st, struct script_data *data)
+{
+ get_val(st, data);
+ assert (!data->is<ScriptDataRetInfo>());
+ if (auto *u = data->get_if<ScriptDataInt>())
+ {
+ AString buf = STRPRINTF("%d"_fmt, u->numi);
+ *data = ScriptDataStr{buf};
+ }
+ return data->get_if<ScriptDataStr>()->str;
+}
+
+/*==========================================
+ * 数値へ変換
+ *------------------------------------------
+ */
+int conv_num(ScriptState *st, struct script_data *data)
+{
+ int rv = 0;
+ get_val(st, data);
+ assert (!data->is<ScriptDataRetInfo>());
+ MATCH_BEGIN (*data)
+ {
+ MATCH_DEFAULT ()
+ {
+ abort();
+ }
+ MATCH_CASE (const ScriptDataStr&, u)
+ {
+ RString p = u.str;
+ rv = atoi(p.c_str());
+ }
+ MATCH_CASE (const ScriptDataInt&, u)
+ {
+ return u.numi;
+ }
+ MATCH_CASE (const ScriptDataPos&, u)
+ {
+ return u.numi;
+ }
+ }
+ MATCH_END ()
+ *data = ScriptDataInt{rv};
+ return rv;
+}
+
+Borrowed<const ScriptBuffer> conv_script(ScriptState *st, struct script_data *data)
+{
+ get_val(st, data);
+ return data->get_if<ScriptDataRetInfo>()->script;
+}
+
+void push_copy(struct script_stack *stack, int pos_)
+{
+ script_data csd = stack->stack_datav[pos_];
+ stack->stack_datav.push_back(csd);
+}
+
+void pop_stack(struct script_stack *stack, int start, int end)
+{
+ auto it = stack->stack_datav.begin();
+ stack->stack_datav.erase(it + start, it + end);
+}
+
+//
+// 実行部main
+//
+/*==========================================
+ * コマンドの読み取り
+ *------------------------------------------
+ */
+static
+ByteCode get_com(ScriptPointer *script)
+{
+ if (static_cast<uint8_t>(script->peek()) >= 0x80)
+ {
+ // synthetic! Does not advance pos yet.
+ return ByteCode::INT;
+ }
+ return script->pop();
+}
+
+/*==========================================
+ * 数値の所得
+ *------------------------------------------
+ */
+static
+int get_num(ScriptPointer *scr)
+{
+ int i = 0;
+ int j = 0;
+ uint8_t val;
+ do
+ {
+ val = static_cast<uint8_t>(scr->pop());
+ i += (val & 0x7f) << j;
+ j += 6;
+ }
+ while (val >= 0xc0);
+ return i;
+}
+
+/*==========================================
+ * スタックから値を取り出す
+ *------------------------------------------
+ */
+static
+int pop_val(ScriptState *st)
+{
+ if (st->stack->stack_datav.empty())
+ return 0;
+ script_data& back = st->stack->stack_datav.back();
+ get_val(st, &back);
+ int rv = 0;
+ if (auto *u = back.get_if<ScriptDataInt>())
+ rv = u->numi;
+ st->stack->stack_datav.pop_back();
+ return rv;
+}
+
+static
+bool isstr(struct script_data& c)
+{
+ return c.is<ScriptDataStr>();
+}
+
+/*==========================================
+ * 加算演算子
+ *------------------------------------------
+ */
+static
+void op_add(ScriptState *st)
+{
+ get_val(st, &st->stack->stack_datav.back());
+ script_data back = st->stack->stack_datav.back();
+ st->stack->stack_datav.pop_back();
+
+ script_data& back1 = st->stack->stack_datav.back();
+ get_val(st, &back1);
+
+ if (!(isstr(back) || isstr(back1)))
+ {
+ back1.get_if<ScriptDataInt>()->numi += back.get_if<ScriptDataInt>()->numi;
+ }
+ else
+ {
+ RString sb = conv_str(st, &back);
+ RString sb1 = conv_str(st, &back1);
+ MString buf;
+ buf += sb1;
+ buf += sb;
+ back1 = ScriptDataStr{.str= AString(buf)};
+ }
+}
+
+/*==========================================
+ * 二項演算子(文字列)
+ *------------------------------------------
+ */
+static
+void op_2str(ScriptState *st, ByteCode op, ZString s1, ZString s2)
+{
+ int a = 0;
+
+ switch (op)
+ {
+ case ByteCode::EQ:
+ a = s1 == s2;
+ break;
+ case ByteCode::NE:
+ a = s1 != s2;
+ break;
+ case ByteCode::GT:
+ a = s1 > s2;
+ break;
+ case ByteCode::GE:
+ a = s1 >= s2;
+ break;
+ case ByteCode::LT:
+ a = s1 < s2;
+ break;
+ case ByteCode::LE:
+ a = s1 <= s2;
+ break;
+ default:
+ PRINTF("illegal string operater\n"_fmt);
+ break;
+ }
+
+ push_int<ScriptDataInt>(st->stack, a);
+}
+
+/*==========================================
+ * 二項演算子(数値)
+ *------------------------------------------
+ */
+static
+void op_2num(ScriptState *st, ByteCode op, int i1, int i2)
+{
+ switch (op)
+ {
+ case ByteCode::SUB:
+ i1 -= i2;
+ break;
+ case ByteCode::MUL:
+ i1 *= i2;
+ break;
+ case ByteCode::DIV:
+ i1 /= i2;
+ break;
+ case ByteCode::MOD:
+ i1 %= i2;
+ break;
+ case ByteCode::AND:
+ i1 &= i2;
+ break;
+ case ByteCode::OR:
+ i1 |= i2;
+ break;
+ case ByteCode::XOR:
+ i1 ^= i2;
+ break;
+ case ByteCode::LAND:
+ i1 = i1 && i2;
+ break;
+ case ByteCode::LOR:
+ i1 = i1 || i2;
+ break;
+ case ByteCode::EQ:
+ i1 = i1 == i2;
+ break;
+ case ByteCode::NE:
+ i1 = i1 != i2;
+ break;
+ case ByteCode::GT:
+ i1 = i1 > i2;
+ break;
+ case ByteCode::GE:
+ i1 = i1 >= i2;
+ break;
+ case ByteCode::LT:
+ i1 = i1 < i2;
+ break;
+ case ByteCode::LE:
+ i1 = i1 <= i2;
+ break;
+ case ByteCode::R_SHIFT:
+ i1 = i1 >> i2;
+ break;
+ case ByteCode::L_SHIFT:
+ i1 = i1 << i2;
+ break;
+ }
+ push_int<ScriptDataInt>(st->stack, i1);
+}
+
+/*==========================================
+ * 二項演算子
+ *------------------------------------------
+ */
+static
+void op_2(ScriptState *st, ByteCode op)
+{
+ // pop_val has unfortunate implications here
+ script_data d2 = st->stack->stack_datav.back();
+ st->stack->stack_datav.pop_back();
+ get_val(st, &d2);
+ script_data d1 = st->stack->stack_datav.back();
+ st->stack->stack_datav.pop_back();
+ get_val(st, &d1);
+
+ if (isstr(d1) && isstr(d2))
+ {
+ // ss => op_2str
+ op_2str(st, op, d1.get_if<ScriptDataStr>()->str, d2.get_if<ScriptDataStr>()->str);
+ }
+ else if (!(isstr(d1) || isstr(d2)))
+ {
+ // ii => op_2num
+ op_2num(st, op, d1.get_if<ScriptDataInt>()->numi, d2.get_if<ScriptDataInt>()->numi);
+ }
+ else
+ {
+ // si,is => error
+ PRINTF("script: op_2: int&str, str&int not allow.\n"_fmt);
+ push_int<ScriptDataInt>(st->stack, 0);
+ }
+}
+
+/*==========================================
+ * 単項演算子
+ *------------------------------------------
+ */
+static
+void op_1num(ScriptState *st, ByteCode op)
+{
+ int i1;
+ i1 = pop_val(st);
+ switch (op)
+ {
+ case ByteCode::NEG:
+ i1 = -i1;
+ break;
+ case ByteCode::NOT:
+ i1 = ~i1;
+ break;
+ case ByteCode::LNOT:
+ i1 = !i1;
+ break;
+ }
+ push_int<ScriptDataInt>(st->stack, i1);
+}
+
+/*==========================================
+ * 関数の実行
+ *------------------------------------------
+ */
+void run_func(ScriptState *st)
+{
+ size_t end_sp = st->stack->stack_datav.size();
+ size_t start_sp = end_sp - 1;
+ while (!st->stack->stack_datav[start_sp].is<ScriptDataArg>())
+ {
+ start_sp--;
+ if (start_sp == 0)
+ {
+ if (battle_config.error_log)
+ PRINTF("function not found\n"_fmt);
+ st->state = ScriptEndState::END;
+ return;
+ }
+ }
+ // the func is before the arg
+ start_sp--;
+ st->start = start_sp;
+ st->end = end_sp;
+
+ if (!st->stack->stack_datav[st->start].is<ScriptDataFuncRef>())
+ {
+ PRINTF("run_func: not function and command! \n"_fmt);
+ st->state = ScriptEndState::END;
+ return;
+ }
+ size_t func = st->stack->stack_datav[st->start].get_if<ScriptDataFuncRef>()->numi;
+
+ if (DEBUG_RUN && battle_config.etc_log)
+ {
+ PRINTF("run_func : %s\n"_fmt,
+ builtin_functions[func].name);
+ PRINTF("stack dump :"_fmt);
+ for (script_data& d : st->stack->stack_datav)
+ {
+ MATCH_BEGIN (d)
+ {
+ MATCH_CASE (const ScriptDataInt&, u)
+ {
+ PRINTF(" int(%d)"_fmt, u.numi);
+ }
+ MATCH_CASE (const ScriptDataRetInfo&, u)
+ {
+ PRINTF(" retinfo(%p)"_fmt, static_cast<const void *>(&*u.script));
+ }
+ MATCH_CASE (const ScriptDataParam&, u)
+ {
+ PRINTF(" param(%d)"_fmt, u.reg.sp());
+ }
+ MATCH_CASE (const ScriptDataVariable&, u)
+ {
+ PRINTF(" name(%s)[%d]"_fmt, variable_names.outtern(u.reg.base()), u.reg.index());
+ }
+ MATCH_CASE (const ScriptDataArg&, u)
+ {
+ (void)u;
+ PRINTF(" arg"_fmt);
+ }
+ MATCH_CASE (const ScriptDataPos&, u)
+ {
+ (void)u;
+ PRINTF(" pos(%d)"_fmt, u.numi);
+ }
+ MATCH_CASE (const ScriptDataStr&, u)
+ {
+ (void)u;
+ PRINTF(" str(%s)"_fmt, u.str);
+ }
+ MATCH_CASE (const ScriptDataFuncRef&, u)
+ {
+ (void)u;
+ PRINTF(" func(%s)"_fmt, builtin_functions[u.numi].name);
+ }
+ }
+ MATCH_END ();
+ }
+ PRINTF("\n"_fmt);
+ }
+ builtin_functions[func].func(st);
+
+ pop_stack(st->stack, start_sp, end_sp);
+
+ if (st->state == ScriptEndState::RETFUNC)
+ {
+ // ユーザー定義関数からの復帰
+ int olddefsp = st->defsp;
+
+ pop_stack(st->stack, st->defsp, start_sp); // 復帰に邪魔なスタック削除
+ if (st->defsp < 4
+ || !st->stack->stack_datav[st->defsp - 1].is<ScriptDataRetInfo>())
+ {
+ PRINTF("script:run_func (return) return without callfunc or callsub!\n"_fmt);
+ st->state = ScriptEndState::END;
+ return;
+ }
+ assert (olddefsp == st->defsp); // pretty sure it hasn't changed yet
+ st->scriptp.code = Some(conv_script(st, &st->stack->stack_datav[olddefsp - 1])); // スクリプトを復元
+ st->scriptp.pos = conv_num(st, &st->stack->stack_datav[olddefsp - 2]); // スクリプト位置の復元
+ st->defsp = conv_num(st, &st->stack->stack_datav[olddefsp - 3]); // 基準スタックポインタを復元
+ // Number of arguments.
+ int i = conv_num(st, &st->stack->stack_datav[olddefsp - 4]); // 引数の数所得
+ assert (i == 0);
+
+ pop_stack(st->stack, olddefsp - 4 - i, olddefsp); // 要らなくなったスタック(引数と復帰用データ)削除
+
+ st->state = ScriptEndState::GOTO;
+ }
+}
+
+/*==========================================
+ * スクリプトの実行メイン部分
+ *------------------------------------------
+ */
+static
+void run_script_main(ScriptState *st, Borrowed<const ScriptBuffer> rootscript)
+{
+ int cmdcount = script_config.check_cmdcount;
+ int gotocount = script_config.check_gotocount;
+ struct script_stack *stack = st->stack;
+
+ st->defsp = stack->stack_datav.size();
+
+ int rerun_pos = st->scriptp.pos;
+ st->state = ScriptEndState::ZERO;
+ while (st->state == ScriptEndState::ZERO)
+ {
+ switch (ByteCode c = get_com(&st->scriptp))
+ {
+ case ByteCode::EOL:
+ if (stack->stack_datav.size() != st->defsp)
+ {
+ if (true)
+ PRINTF("stack.sp (%zu) != default (%d)\n"_fmt,
+ stack->stack_datav.size(),
+ st->defsp);
+ abort();
+ }
+ rerun_pos = st->scriptp.pos;
+ break;
+ case ByteCode::INT:
+ // synthesized!
+ push_int<ScriptDataInt>(stack, get_num(&st->scriptp));
+ break;
+
+ case ByteCode::POS:
+ case ByteCode::VARIABLE:
+ case ByteCode::FUNC_REF:
+ case ByteCode::PARAM:
+ // Note that these 3 have *very* different meanings,
+ // despite being encoded similarly.
+ {
+ int arg = 0;
+ arg |= static_cast<uint8_t>(st->scriptp.pop()) << 0;
+ arg |= static_cast<uint8_t>(st->scriptp.pop()) << 8;
+ arg |= static_cast<uint8_t>(st->scriptp.pop()) << 16;
+ switch(c)
+ {
+ case ByteCode::POS:
+ push_int<ScriptDataPos>(stack, arg);
+ break;
+ case ByteCode::VARIABLE:
+ push_reg<ScriptDataVariable>(stack, SIR::from(arg));
+ break;
+ case ByteCode::FUNC_REF:
+ push_int<ScriptDataFuncRef>(stack, arg);
+ break;
+ case ByteCode::PARAM:
+ SP arg_sp = static_cast<SP>(arg);
+ push_reg<ScriptDataParam>(stack, SIR::from(arg_sp));
+ break;
+ }
+ }
+ break;
+ case ByteCode::ARG:
+ push_int<ScriptDataArg>(stack, 0);
+ break;
+ case ByteCode::STR:
+ push_str<ScriptDataStr>(stack, st->scriptp.pops());
+ break;
+ case ByteCode::FUNC:
+ run_func(st);
+ if (st->state == ScriptEndState::GOTO)
+ {
+ rerun_pos = st->scriptp.pos;
+ st->state = ScriptEndState::ZERO;
+ if (st->freeloop != 1 && gotocount > 0 && (--gotocount) <= 0)
+ {
+ PRINTF("run_script: infinity loop !\n"_fmt);
+ st->state = ScriptEndState::END;
+ }
+ }
+ break;
+
+ case ByteCode::ADD:
+ op_add(st);
+ break;
+
+ case ByteCode::SUB:
+ case ByteCode::MUL:
+ case ByteCode::DIV:
+ case ByteCode::MOD:
+ case ByteCode::EQ:
+ case ByteCode::NE:
+ case ByteCode::GT:
+ case ByteCode::GE:
+ case ByteCode::LT:
+ case ByteCode::LE:
+ case ByteCode::AND:
+ case ByteCode::OR:
+ case ByteCode::XOR:
+ case ByteCode::LAND:
+ case ByteCode::LOR:
+ case ByteCode::R_SHIFT:
+ case ByteCode::L_SHIFT:
+ op_2(st, c);
+ break;
+
+ case ByteCode::NEG:
+ case ByteCode::NOT:
+ case ByteCode::LNOT:
+ op_1num(st, c);
+ break;
+
+ case ByteCode::NOP:
+ st->state = ScriptEndState::END;
+ break;
+
+ default:
+ if (battle_config.error_log)
+ PRINTF("unknown command : %d @ %zu\n"_fmt,
+ c, st->scriptp.pos);
+ st->state = ScriptEndState::END;
+ break;
+ }
+ if (st->freeloop != 1 && cmdcount > 0 && (--cmdcount) <= 0)
+ {
+ PRINTF("run_script: infinity loop !\n"_fmt);
+ st->state = ScriptEndState::END;
+ }
+ }
+ switch (st->state)
+ {
+ case ScriptEndState::STOP:
+ break;
+ case ScriptEndState::END:
+ {
+ dumb_ptr<map_session_data> sd = map_id2sd(st->rid);
+ st->scriptp.code = None;
+ st->scriptp.pos = -1;
+ if (sd && sd->npc_id == st->oid)
+ npc_event_dequeue(sd);
+ }
+ break;
+ case ScriptEndState::RERUNLINE:
+ st->scriptp.pos = rerun_pos;
+ break;
+ }
+
+ if (st->state != ScriptEndState::END)
+ {
+ // 再開するためにスタック情報を保存
+ dumb_ptr<map_session_data> sd = map_id2sd(st->rid);
+ if (sd)
+ {
+ sd->npc_stackbuf = stack->stack_datav;
+ sd->npc_script = st->scriptp.code;
+ // sd->npc_pos is set later ... ???
+ sd->npc_scriptroot = Some(rootscript);
+ }
+ }
+}
+
+/*==========================================
+ * スクリプトの実行
+ *------------------------------------------
+ */
+int run_script(ScriptPointer sp, BlockId rid, BlockId oid)
+{
+ return run_script_l(sp, rid, oid, nullptr);
+}
+
+int run_script_l(ScriptPointer sp, BlockId rid, BlockId oid,
+ Slice<argrec_t> args)
+{
+ struct script_stack stack;
+ ScriptState st;
+ dumb_ptr<map_session_data> sd = map_id2sd(rid);
+ P<const ScriptBuffer> rootscript = TRY_UNWRAP(sp.code, return -1);
+ int i;
+ if (sp.pos >> 24)
+ return -1;
+
+ if (sd && !sd->npc_stackbuf.empty() && sd->npc_scriptroot == Some(rootscript))
+ {
+ // 前回のスタックを復帰
+ sp.code = sd->npc_script;
+ stack.stack_datav = std::move(sd->npc_stackbuf);
+ }
+ st.stack = &stack;
+ st.scriptp = sp;
+ st.rid = rid;
+ st.oid = oid;
+ for (i = 0; i < args.size(); i++)
+ {
+ if (args[i].name.back() == '$')
+ pc_setregstr(sd, SIR::from(variable_names.intern(args[i].name)), args[i].v.s);
+ else
+ pc_setreg(sd, SIR::from(variable_names.intern(args[i].name)), args[i].v.i);
+ }
+ run_script_main(&st, rootscript);
+
+ stack.stack_datav.clear();
+ return st.scriptp.pos;
+}
+
+void set_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e, int val)
+{
+ size_t k = variable_names.intern(var);
+ SIR reg = SIR::from(k, e);
+ set_reg(sd, VariableCode::VARIABLE, reg, val);
+}
+void set_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e, XString val)
+{
+ size_t k = variable_names.intern(var);
+ SIR reg = SIR::from(k, e);
+ set_reg(sd, VariableCode::VARIABLE, reg, val);
+}
+int get_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e)
+{
+ size_t k = variable_names.intern(var);
+ SIR reg = SIR::from(k, e);
+ struct script_data dat = ScriptDataVariable{.reg= reg};
+ get_val(sd, &dat);
+ if (auto *u = dat.get_if<ScriptDataInt>())
+ return u->numi;
+ PRINTF("Warning: you lied about the type and I'm too lazy to fix it!"_fmt);
+ return 0;
+}
+ZString get_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e)
+{
+ size_t k = variable_names.intern(var);
+ SIR reg = SIR::from(k, e);
+ struct script_data dat = ScriptDataVariable{.reg= reg};
+ get_val(sd, &dat);
+ if (auto *u = dat.get_if<ScriptDataStr>())
+ // this is almost certainly a memory leak after CONSTSTR removal
+ return u->str;
+ PRINTF("Warning: you lied about the type and I can't fix it!"_fmt);
+ return ZString();
+}
+} // namespace map
+} // namespace tmwa
diff --git a/src/map/script-call.hpp b/src/map/script-call.hpp
new file mode 100644
index 0000000..d494326
--- /dev/null
+++ b/src/map/script-call.hpp
@@ -0,0 +1,67 @@
+#pragma once
+// script-call.hpp - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "script-call.t.hpp"
+
+#include "fwd.hpp"
+
+#include "../compat/borrow.hpp"
+
+#include "script-buffer.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+enum class ByteCode : uint8_t;
+
+// implemented in script-parse.cpp because reasons
+struct ScriptPointer
+{
+ Option<Borrowed<const ScriptBuffer>> code;
+ size_t pos;
+
+ ScriptPointer()
+ : code(None)
+ , pos()
+ {}
+ ScriptPointer(Borrowed<const ScriptBuffer> c, size_t p)
+ : code(Some(c))
+ , pos(p)
+ {}
+
+ ByteCode peek() const;
+ ByteCode pop();
+ ZString pops();
+};
+
+int run_script_l(ScriptPointer, BlockId, BlockId, Slice<argrec_t> args);
+int run_script(ScriptPointer, BlockId, BlockId);
+
+void set_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e, int val);
+void set_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e, XString val);
+
+int get_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e);
+ZString get_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e);
+} // namespace map
+} // namespace tmwa
diff --git a/src/map/script-call.t.hpp b/src/map/script-call.t.hpp
new file mode 100644
index 0000000..5ef7de6
--- /dev/null
+++ b/src/map/script-call.t.hpp
@@ -0,0 +1,48 @@
+#pragma once
+// script-call.t.hpp - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+#include "../strings/zstring.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+struct argrec_t
+{
+ ZString name;
+ union _aru
+ {
+ int i;
+ ZString s;
+
+ _aru(int n) : i(n) {}
+ _aru(ZString z) : s(z) {}
+ } v;
+
+ argrec_t(ZString n, int i) : name(n), v(i) {}
+ argrec_t(ZString n, ZString z) : name(n), v(z) {}
+};
+} // namespace map
+} // namespace tmwa
diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp
new file mode 100644
index 0000000..3291cfc
--- /dev/null
+++ b/src/map/script-fun.cpp
@@ -0,0 +1,3164 @@
+#include "script-fun.hpp"
+// script-fun.cpp - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011 Chuck Miller
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2013 wushin
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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/fun.hpp"
+
+#include "../generic/db.hpp"
+#include "../generic/dumb_ptr.hpp"
+#include "../generic/intern-pool.hpp"
+#include "../generic/random.hpp"
+
+#include "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
+
+#include "../net/timer.hpp"
+
+#include "../proto2/net-HumanTimeDiff.hpp"
+
+#include "../high/core.hpp"
+#include "../high/extract_mmo.hpp"
+
+#include "atcommand.hpp"
+#include "battle.hpp"
+#include "battle_conf.hpp"
+#include "chrif.hpp"
+#include "clif.hpp"
+#include "globals.hpp"
+#include "intif.hpp"
+#include "itemdb.hpp"
+#include "magic-interpreter-base.hpp"
+#include "map.hpp"
+#include "mob.hpp"
+#include "npc.hpp"
+#include "party.hpp"
+#include "pc.hpp"
+#include "script-call-internal.hpp"
+#include "script-parse-internal.hpp"
+#include "script-persist.hpp"
+#include "skill.hpp"
+#include "storage.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+static
+Array<LString, 11> pos_str //=
+{{
+ "Head"_s,
+ "Body"_s,
+ "Left hand"_s,
+ "Right hand"_s,
+ "Robe"_s,
+ "Shoes"_s,
+ "Accessory 1"_s,
+ "Accessory 2"_s,
+ "Head 2"_s,
+ "Head 3"_s,
+ "Not Equipped"_s,
+}};
+
+#define AARG(n) (st->stack->stack_datav[st->start + 2 + (n)])
+#define HARG(n) (st->end > st->start + 2 + (n))
+
+//
+// 埋め込み関数
+//
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_mes(ScriptState *st)
+{
+ RString mes = conv_str(st, &AARG(0));
+ clif_scriptmes(script_rid2sd(st), st->oid, mes);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_goto(ScriptState *st)
+{
+ if (!AARG(0).is<ScriptDataPos>())
+ {
+ PRINTF("script: goto: that's not a label!\n"_fmt);
+ st->state = ScriptEndState::END;
+ return;
+ }
+
+ st->scriptp.pos = conv_num(st, &AARG(0));
+ st->state = ScriptEndState::GOTO;
+}
+
+/*==========================================
+ * ユーザー定義関数の呼び出し
+ *------------------------------------------
+ */
+static
+void builtin_callfunc(ScriptState *st)
+{
+ RString str = conv_str(st, &AARG(0));
+ Option<P<const ScriptBuffer>> scr_ = userfunc_db.get(str);
+
+ OMATCH_BEGIN (scr_)
+ {
+ OMATCH_CASE_SOME (scr)
+ {
+ int j = 0;
+ assert (st->start + 3 == st->end);
+#if 0
+ for (int i = st->start + 3; i < st->end; i++, j++)
+ push_copy(st->stack, i);
+#endif
+
+ push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ
+ push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ
+ push_int<ScriptDataInt>(st->stack, st->scriptp.pos); // 現在のスクリプト位置をプッシュ
+ push_script<ScriptDataRetInfo>(st->stack, TRY_UNWRAP(st->scriptp.code, abort())); // 現在のスクリプトをプッシュ
+
+ st->scriptp = ScriptPointer(scr, 0);
+ st->defsp = st->start + 4 + j;
+ st->state = ScriptEndState::GOTO;
+ }
+ OMATCH_CASE_NONE ()
+ {
+ PRINTF("script: callfunc: function not found! [%s]\n"_fmt, str);
+ st->state = ScriptEndState::END;
+ }
+ }
+ OMATCH_END ();
+}
+
+/*==========================================
+ * サブルーティンの呼び出し
+ *------------------------------------------
+ */
+static
+void builtin_callsub(ScriptState *st)
+{
+ int pos_ = conv_num(st, &AARG(0));
+ int j = 0;
+ assert (st->start + 3 == st->end);
+#if 0
+ for (int i = st->start + 3; i < st->end; i++, j++)
+ push_copy(st->stack, i);
+#endif
+
+ push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ
+ push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ
+ push_int<ScriptDataInt>(st->stack, st->scriptp.pos); // 現在のスクリプト位置をプッシュ
+ push_script<ScriptDataRetInfo>(st->stack, TRY_UNWRAP(st->scriptp.code, abort())); // 現在のスクリプトをプッシュ
+
+ st->scriptp.pos = pos_;
+ st->defsp = st->start + 4 + j;
+ st->state = ScriptEndState::GOTO;
+}
+
+/*==========================================
+ * サブルーチン/ユーザー定義関数の終了
+ *------------------------------------------
+ */
+static
+void builtin_return(ScriptState *st)
+{
+#if 0
+ if (HARG(0))
+ { // 戻り値有り
+ push_copy(st->stack, st->start + 2);
+ }
+#endif
+ st->state = ScriptEndState::RETFUNC;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_next(ScriptState *st)
+{
+ st->state = ScriptEndState::STOP;
+ clif_scriptnext(script_rid2sd(st), st->oid);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_close(ScriptState *st)
+{
+ st->state = ScriptEndState::END;
+ clif_scriptclose(script_rid2sd(st), st->oid);
+}
+
+static
+void builtin_close2(ScriptState *st)
+{
+ st->state = ScriptEndState::STOP;
+ clif_scriptclose(script_rid2sd(st), st->oid);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_menu(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ if (sd->state.menu_or_input == 0)
+ {
+ // First half: show menu.
+ st->state = ScriptEndState::RERUNLINE;
+ sd->state.menu_or_input = 1;
+
+ MString buf;
+ for (int i = 0; i < (st->end - (st->start + 2)) / 2; i++)
+ {
+ RString choice_str = conv_str(st, &AARG(i * 2 + 0));
+ if (!choice_str)
+ break;
+ buf += choice_str;
+ buf += ':';
+ }
+
+ clif_scriptmenu(script_rid2sd(st), st->oid, AString(buf));
+ }
+ else
+ {
+ // Rerun: item is chosen from menu.
+ if (sd->npc_menu == 0xff)
+ {
+ // cancel
+ sd->state.menu_or_input = 0;
+ st->state = ScriptEndState::END;
+ return;
+ }
+
+ // Actually jump to the label.
+ // Logic change: menu_choices is the *total* number of labels,
+ // not just the displayed number that ends with the "".
+ // (Would it be better to pop the stack before rerunning?)
+ int menu_choices = (st->end - (st->start + 2)) / 2;
+ pc_setreg(sd, SIR::from(variable_names.intern("@menu"_s)), sd->npc_menu);
+ sd->state.menu_or_input = 0;
+ if (sd->npc_menu > 0 && sd->npc_menu <= menu_choices)
+ {
+ int arg_index = (sd->npc_menu - 1) * 2 + 1;
+ if (!AARG(arg_index).is<ScriptDataPos>())
+ {
+ st->state = ScriptEndState::END;
+ return;
+ }
+ st->scriptp.pos = AARG(arg_index).get_if<ScriptDataPos>()->numi;
+ st->state = ScriptEndState::GOTO;
+ }
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_rand(ScriptState *st)
+{
+ if (HARG(1))
+ {
+ int min = conv_num(st, &AARG(0));
+ int max = conv_num(st, &AARG(1));
+ if (min > max)
+ std::swap(max, min);
+ push_int<ScriptDataInt>(st->stack, random_::in(min, max));
+ }
+ else
+ {
+ int range = conv_num(st, &AARG(0));
+ push_int<ScriptDataInt>(st->stack, range <= 0 ? 0 : random_::to(range));
+ }
+}
+
+/*==========================================
+ * Check whether the PC is at the specified location
+ *------------------------------------------
+ */
+static
+void builtin_isat(ScriptState *st)
+{
+ int x, y;
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ x = conv_num(st, &AARG(1));
+ y = conv_num(st, &AARG(2));
+
+ if (!sd)
+ return;
+
+ push_int<ScriptDataInt>(st->stack,
+ (x == sd->bl_x) && (y == sd->bl_y)
+ && (str == sd->bl_m->name_));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_warp(ScriptState *st)
+{
+ int x, y;
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ x = conv_num(st, &AARG(1));
+ y = conv_num(st, &AARG(2));
+ pc_setpos(sd, str, x, y, BeingRemoveWhy::GONE);
+}
+
+/*==========================================
+ * エリア指定ワープ
+ *------------------------------------------
+ */
+static
+void builtin_areawarp_sub(dumb_ptr<block_list> bl, MapName mapname, int x, int y)
+{
+ dumb_ptr<map_session_data> sd = bl->is_player();
+ pc_setpos(sd, mapname, x, y, BeingRemoveWhy::GONE);
+}
+
+static
+void builtin_areawarp(ScriptState *st)
+{
+ int x, y;
+ int x0, y0, x1, y1;
+
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ x0 = conv_num(st, &AARG(1));
+ y0 = conv_num(st, &AARG(2));
+ x1 = conv_num(st, &AARG(3));
+ y1 = conv_num(st, &AARG(4));
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(5))));
+ x = conv_num(st, &AARG(6));
+ y = conv_num(st, &AARG(7));
+
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
+
+ map_foreachinarea(std::bind(builtin_areawarp_sub, ph::_1, str, x, y),
+ m,
+ x0, y0,
+ x1, y1,
+ BL::PC);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_heal(ScriptState *st)
+{
+ int hp, sp;
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ hp = conv_num(st, &AARG(0));
+ sp = conv_num(st, &AARG(1));
+
+ if(sd != nullptr && (sd->status.hp < 1 && hp > 0)){
+ pc_setstand(sd);
+ if (battle_config.player_invincible_time > interval_t::zero())
+ pc_setinvincibletimer(sd, battle_config.player_invincible_time);
+ clif_resurrection(sd, 1);
+ }
+
+ if(HARG(2) && bool(conv_num(st, &AARG(2))) && hp > 0)
+ pc_itemheal(sd, hp, sp);
+ else
+ pc_heal(sd, hp, sp);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_input(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = nullptr;
+ script_data& scrd = AARG(0);
+ assert (scrd.is<ScriptDataVariable>());
+
+ SIR reg = scrd.get_if<ScriptDataVariable>()->reg;
+ ZString name = variable_names.outtern(reg.base());
+// char prefix = name.front();
+ char postfix = name.back();
+
+ sd = script_rid2sd(st);
+ if (sd->state.menu_or_input)
+ {
+ // Second time (rerun)
+ sd->state.menu_or_input = 0;
+ if (postfix == '$')
+ {
+ set_reg(sd, VariableCode::VARIABLE, reg, sd->npc_str);
+ }
+ else
+ {
+ //commented by Lupus (check Value Number Input fix in clif.c)
+ //** Fix by fritz :X keeps people from abusing old input bugs
+ // wtf?
+ if (sd->npc_amount < 0) //** If input amount is less then 0
+ {
+ clif_tradecancelled(sd); // added "Deal has been cancelled" message by Valaris
+ builtin_close(st); //** close
+ }
+
+ set_reg(sd, VariableCode::VARIABLE, reg, sd->npc_amount);
+ }
+ }
+ else
+ {
+ // First time - send prompt to client, then wait
+ st->state = ScriptEndState::RERUNLINE;
+ if (postfix == '$')
+ clif_scriptinputstr(sd, st->oid);
+ else
+ clif_scriptinput(sd, st->oid);
+ sd->state.menu_or_input = 1;
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_if (ScriptState *st)
+{
+ int sel, i;
+
+ sel = conv_num(st, &AARG(0));
+ if (!sel)
+ return;
+
+ // 関数名をコピー
+ push_copy(st->stack, st->start + 3);
+ // 間に引数マーカを入れて
+ push_int<ScriptDataArg>(st->stack, 0);
+ // 残りの引数をコピー
+ for (i = st->start + 4; i < st->end; i++)
+ {
+ push_copy(st->stack, i);
+ }
+ run_func(st);
+}
+
+/*==========================================
+ * 変数設定
+ *------------------------------------------
+ */
+static
+void builtin_set(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = nullptr;
+ if (auto *u = AARG(0).get_if<ScriptDataParam>())
+ {
+ SIR reg = u->reg;
+ sd = script_rid2sd(st);
+
+ int val = conv_num(st, &AARG(1));
+ set_reg(sd, VariableCode::PARAM, reg, val);
+ return;
+ }
+
+ SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
+
+ ZString name = variable_names.outtern(reg.base());
+ char prefix = name.front();
+ char postfix = name.back();
+
+ if (prefix != '$')
+ sd = script_rid2sd(st);
+
+ if (postfix == '$')
+ {
+ // 文字列
+ RString str = conv_str(st, &AARG(1));
+ set_reg(sd, VariableCode::VARIABLE, reg, str);
+ }
+ else
+ {
+ // 数値
+ int val = conv_num(st, &AARG(1));
+ set_reg(sd, VariableCode::VARIABLE, reg, val);
+ }
+
+}
+
+/*==========================================
+ * 配列変数設定
+ *------------------------------------------
+ */
+static
+void builtin_setarray(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = nullptr;
+ SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
+ ZString name = variable_names.outtern(reg.base());
+ char prefix = name.front();
+ char postfix = name.back();
+
+ if (prefix != '$' && prefix != '@')
+ {
+ PRINTF("builtin_setarray: illegal scope!\n"_fmt);
+ return;
+ }
+ if (prefix != '$')
+ sd = script_rid2sd(st);
+
+ for (int j = 0, i = 1; i < st->end - st->start - 2 && j < 256; i++, j++)
+ {
+ if (postfix == '$')
+ set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), conv_str(st, &AARG(i)));
+ else
+ set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), conv_num(st, &AARG(i)));
+ }
+}
+
+/*==========================================
+ * 配列変数クリア
+ *------------------------------------------
+ */
+static
+void builtin_cleararray(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = nullptr;
+ SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
+ ZString name = variable_names.outtern(reg.base());
+ char prefix = name.front();
+ char postfix = name.back();
+ int sz = conv_num(st, &AARG(2));
+
+ if (prefix != '$' && prefix != '@')
+ {
+ PRINTF("builtin_cleararray: illegal scope!\n"_fmt);
+ return;
+ }
+ if (prefix != '$')
+ sd = script_rid2sd(st);
+
+ for (int i = 0; i < sz; i++)
+ {
+ if (postfix == '$')
+ set_reg(sd, VariableCode::VARIABLE, reg.iplus(i), conv_str(st, &AARG(1)));
+ else
+ set_reg(sd, VariableCode::VARIABLE, reg.iplus(i), conv_num(st, &AARG(1)));
+ }
+
+}
+
+/*==========================================
+ * 配列変数のサイズ所得
+ *------------------------------------------
+ */
+static
+int getarraysize(ScriptState *st, SIR reg)
+{
+ int i = reg.index(), c = i;
+ for (; i < 256; i++)
+ {
+ struct script_data vd = get_val2(st, reg.iplus(i));
+ MATCH_BEGIN (vd)
+ {
+ MATCH_CASE (const ScriptDataStr&, u)
+ {
+ if (u.str[0])
+ c = i;
+ continue;
+ }
+ MATCH_CASE (const ScriptDataInt&, u)
+ {
+ if (u.numi)
+ c = i;
+ continue;
+ }
+ }
+ MATCH_END ();
+ abort();
+ }
+ return c + 1;
+}
+
+static
+void builtin_getarraysize(ScriptState *st)
+{
+ SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
+ ZString name = variable_names.outtern(reg.base());
+ char prefix = name.front();
+
+ if (prefix != '$' && prefix != '@')
+ {
+ PRINTF("builtin_copyarray: illegal scope!\n"_fmt);
+ return;
+ }
+
+ push_int<ScriptDataInt>(st->stack, getarraysize(st, reg));
+}
+
+/*==========================================
+ * 指定要素を表す値(キー)を所得する
+ *------------------------------------------
+ */
+static
+void builtin_getelementofarray(ScriptState *st)
+{
+ if (auto *u = AARG(0).get_if<ScriptDataVariable>())
+ {
+ int i = conv_num(st, &AARG(1));
+ if (i > 255 || i < 0)
+ {
+ PRINTF("script: getelementofarray (operator[]): param2 illegal number: %d\n"_fmt,
+ i);
+ push_int<ScriptDataInt>(st->stack, 0);
+ }
+ else
+ {
+ push_reg<ScriptDataVariable>(st->stack,
+ u->reg.iplus(i));
+ }
+ }
+ else
+ {
+ PRINTF("script: getelementofarray (operator[]): param1 not named!\n"_fmt);
+ push_int<ScriptDataInt>(st->stack, 0);
+ }
+}
+
+static
+void builtin_wgm(ScriptState *st)
+{
+ ZString message = ZString(conv_str(st, &AARG(0)));
+
+ intif_wis_message_to_gm(WISP_SERVER_NAME,
+ battle_config.hack_info_GM_level,
+ STRPRINTF("[GM] %s"_fmt, message));
+}
+
+static
+void builtin_gmlog(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ ZString message = ZString(conv_str(st, &AARG(0)));
+ log_atcommand(sd, STRPRINTF("{SCRIPT} %s"_fmt, message));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_setlook(ScriptState *st)
+{
+ LOOK type = LOOK(conv_num(st, &AARG(0)));
+ int val = conv_num(st, &AARG(1));
+
+ pc_changelook(script_rid2sd(st), type, val);
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_countitem(ScriptState *st)
+{
+ ItemNameId nameid;
+ int count = 0;
+ dumb_ptr<map_session_data> sd;
+
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data = &AARG(0);
+ get_val(st, data);
+ if (data->is<ScriptDataStr>())
+ {
+ ZString name = ZString(conv_str(st, data));
+ Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
+ OMATCH_BEGIN_SOME (item_data, item_data_)
+ {
+ nameid = item_data->nameid;
+ }
+ OMATCH_END ();
+ }
+ else
+ nameid = wrap<ItemNameId>(conv_num(st, data));
+
+ if (nameid)
+ {
+ for (IOff0 i : IOff0::iter())
+ {
+ if (sd->status.inventory[i].nameid == nameid)
+ count += sd->status.inventory[i].amount;
+ }
+ }
+ else
+ {
+ if (battle_config.error_log)
+ PRINTF("wrong item ID: countitem (%i)\n"_fmt, nameid);
+ }
+ push_int<ScriptDataInt>(st->stack, count);
+
+}
+
+/*==========================================
+ * 重量チェック
+ *------------------------------------------
+ */
+static
+void builtin_checkweight(ScriptState *st)
+{
+ ItemNameId nameid;
+ int amount;
+ dumb_ptr<map_session_data> sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data = &AARG(0);
+ get_val(st, data);
+ if (data->is<ScriptDataStr>())
+ {
+ ZString name = ZString(conv_str(st, data));
+ Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
+ OMATCH_BEGIN_SOME (item_data, item_data_)
+ {
+ nameid = item_data->nameid;
+ }
+ OMATCH_END ();
+ }
+ else
+ nameid = wrap<ItemNameId>(conv_num(st, data));
+
+ amount = conv_num(st, &AARG(1));
+ if (amount <= 0 || !nameid)
+ {
+ //If it gets the wrong item ID or the amount<=0, don't count its weight (assume it's a non-existent item)
+ push_int<ScriptDataInt>(st->stack, 0);
+ return;
+ }
+
+ if (itemdb_weight(nameid) * amount + sd->weight > sd->max_weight)
+ {
+ push_int<ScriptDataInt>(st->stack, 0);
+ }
+ else
+ {
+ push_int<ScriptDataInt>(st->stack, 1);
+ }
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_getitem(ScriptState *st)
+{
+ ItemNameId nameid;
+ int amount;
+ dumb_ptr<map_session_data> sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data = &AARG(0);
+ get_val(st, data);
+ if (data->is<ScriptDataStr>())
+ {
+ ZString name = ZString(conv_str(st, data));
+ Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
+ OMATCH_BEGIN_SOME (item_data, item_data_)
+ {
+ nameid = item_data->nameid;
+ }
+ OMATCH_END ();
+ }
+ else
+ nameid = wrap<ItemNameId>(conv_num(st, data));
+
+ if ((amount =
+ conv_num(st, &AARG(1))) <= 0)
+ {
+ return; //return if amount <=0, skip the useles iteration
+ }
+
+ if (nameid)
+ {
+ Item item_tmp {};
+ item_tmp.nameid = nameid;
+ if (HARG(3)) //アイテムを指定したIDに渡す
+ sd = map_id2sd(wrap<BlockId>(conv_num(st, &AARG(3))));
+ if (sd == nullptr) //アイテムを渡す相手がいなかったらお帰り
+ return;
+ PickupFail flag;
+ if ((flag = pc_additem(sd, &item_tmp, amount)) != PickupFail::OKAY)
+ {
+ clif_additem(sd, IOff0::from(0), 0, flag);
+ map_addflooritem(&item_tmp, amount,
+ sd->bl_m, sd->bl_x, sd->bl_y,
+ nullptr, nullptr, nullptr);
+ }
+ }
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_makeitem(ScriptState *st)
+{
+ ItemNameId nameid;
+ int amount;
+ int x, y;
+ dumb_ptr<map_session_data> sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data = &AARG(0);
+ get_val(st, data);
+ if (data->is<ScriptDataStr>())
+ {
+ ZString name = ZString(conv_str(st, data));
+ Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
+ OMATCH_BEGIN_SOME (item_data, item_data_)
+ {
+ nameid = item_data->nameid;
+ }
+ OMATCH_END ();
+ }
+ else
+ nameid = wrap<ItemNameId>(conv_num(st, data));
+
+ amount = conv_num(st, &AARG(1));
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(2))));
+ x = conv_num(st, &AARG(3));
+ y = conv_num(st, &AARG(4));
+
+ P<map_local> m = ((sd && mapname == MOB_THIS_MAP)
+ ? sd->bl_m
+ : TRY_UNWRAP(map_mapname2mapid(mapname), return));
+
+ if (nameid)
+ {
+ Item item_tmp {};
+ item_tmp.nameid = nameid;
+
+ map_addflooritem(&item_tmp, amount, m, x, y, nullptr, nullptr, nullptr);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_delitem(ScriptState *st)
+{
+ ItemNameId nameid;
+ int amount;
+ dumb_ptr<map_session_data> sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data = &AARG(0);
+ get_val(st, data);
+ if (data->is<ScriptDataStr>())
+ {
+ ZString name = ZString(conv_str(st, data));
+ Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
+ OMATCH_BEGIN_SOME (item_data, item_data_)
+ {
+ nameid = item_data->nameid;
+ }
+ OMATCH_END ();
+ }
+ else
+ nameid = wrap<ItemNameId>(conv_num(st, data));
+
+ amount = conv_num(st, &AARG(1));
+
+ if (!nameid || amount <= 0)
+ {
+ //By Lupus. Don't run FOR if you've got the wrong item ID or amount<=0
+ return;
+ }
+
+ for (IOff0 i : IOff0::iter())
+ {
+ if (sd->status.inventory[i].nameid == nameid)
+ {
+ if (sd->status.inventory[i].amount >= amount)
+ {
+ pc_delitem(sd, i, amount, 0);
+ break;
+ }
+ else
+ {
+ amount -= sd->status.inventory[i].amount;
+ if (amount == 0)
+ amount = sd->status.inventory[i].amount;
+ pc_delitem(sd, i, amount, 0);
+ break;
+ }
+ }
+ }
+
+}
+
+/*==========================================
+ *キャラ関係のID取得
+ *------------------------------------------
+ */
+static
+void builtin_getcharid(ScriptState *st)
+{
+ int num;
+ dumb_ptr<map_session_data> sd;
+
+ num = conv_num(st, &AARG(0));
+ if (HARG(1))
+ sd = map_nick2sd(stringish<CharName>(ZString(conv_str(st, &AARG(1)))));
+ else
+ sd = script_rid2sd(st);
+ if (sd == nullptr)
+ {
+ push_int<ScriptDataInt>(st->stack, -1);
+ return;
+ }
+ if (num == 0)
+ push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status_key.char_id));
+ if (num == 1)
+ push_int<ScriptDataInt>(st->stack, unwrap<PartyId>(sd->status.party_id));
+ if (num == 2)
+ push_int<ScriptDataInt>(st->stack, 0/*guild_id*/);
+ if (num == 3)
+ push_int<ScriptDataInt>(st->stack, unwrap<AccountId>(sd->status_key.account_id));
+}
+
+/*==========================================
+ *指定IDのPT名取得
+ *------------------------------------------
+ */
+static
+RString builtin_getpartyname_sub(PartyId party_id)
+{
+ Option<PartyPair> p = party_search(party_id);
+
+ return p.pmd_pget(&PartyMost::name).copy_or(PartyName());
+}
+
+/*==========================================
+ * キャラクタの名前
+ *------------------------------------------
+ */
+static
+void builtin_strcharinfo(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd;
+ int num;
+
+ sd = script_rid2sd(st);
+ num = conv_num(st, &AARG(0));
+ if (num == 0)
+ {
+ RString buf = sd->status_key.name.to__actual();
+ push_str<ScriptDataStr>(st->stack, buf);
+ }
+ if (num == 1)
+ {
+ RString buf = builtin_getpartyname_sub(sd->status.party_id);
+ if (buf)
+ push_str<ScriptDataStr>(st->stack, buf);
+ else
+ push_str<ScriptDataStr>(st->stack, ""_s);
+ }
+ if (num == 2)
+ {
+ // was: guild name
+ push_str<ScriptDataStr>(st->stack, ""_s);
+ }
+
+}
+
+// indexed by the equip_* in db/const.txt
+// TODO change to use EQUIP
+static
+Array<EPOS, 11> equip //=
+{{
+ EPOS::HAT,
+ EPOS::MISC1,
+ EPOS::SHIELD,
+ EPOS::WEAPON,
+ EPOS::GLOVES,
+ EPOS::SHOES,
+ EPOS::CAPE,
+ EPOS::MISC2,
+ EPOS::TORSO,
+ EPOS::LEGS,
+ EPOS::ARROW,
+}};
+
+/*==========================================
+ * GetEquipID(Pos); Pos: 1-10
+ *------------------------------------------
+ */
+static
+void builtin_getequipid(ScriptState *st)
+{
+ int num;
+ dumb_ptr<map_session_data> sd;
+
+ sd = script_rid2sd(st);
+ if (sd == nullptr)
+ {
+ PRINTF("getequipid: sd == nullptr\n"_fmt);
+ return;
+ }
+ num = conv_num(st, &AARG(0));
+ IOff0 i = pc_checkequip(sd, equip[num - 1]);
+ if (i.ok())
+ {
+ Option<P<struct item_data>> item_ = sd->inventory_data[i];
+ OMATCH_BEGIN (item_)
+ {
+ OMATCH_CASE_SOME (item)
+ {
+ push_int<ScriptDataInt>(st->stack, unwrap<ItemNameId>(item->nameid));
+ }
+ OMATCH_CASE_NONE ()
+ {
+ push_int<ScriptDataInt>(st->stack, 0);
+ }
+ }
+ OMATCH_END ();
+ }
+ else
+ {
+ push_int<ScriptDataInt>(st->stack, -1);
+ }
+}
+
+/*==========================================
+ * freeloop
+ *------------------------------------------
+ */
+static
+void builtin_freeloop(ScriptState *st)
+{
+ int num;
+ num = conv_num(st, &AARG(0));
+ if(num == 1)
+ {
+ st->freeloop = 1;
+ }
+ else
+ {
+ st->freeloop = 0;
+ }
+}
+
+/*==========================================
+ * 装備名文字列(精錬メニュー用)
+ *------------------------------------------
+ */
+static
+void builtin_getequipname(ScriptState *st)
+{
+ int num;
+ dumb_ptr<map_session_data> sd;
+
+ AString buf;
+
+ sd = script_rid2sd(st);
+ num = conv_num(st, &AARG(0));
+ IOff0 i = pc_checkequip(sd, equip[num - 1]);
+ if (i.ok())
+ {
+ Option<P<struct item_data>> item_ = sd->inventory_data[i];
+ OMATCH_BEGIN (item_)
+ {
+ OMATCH_CASE_SOME (item)
+ {
+ buf = STRPRINTF("%s-[%s]"_fmt, pos_str[num - 1], item->jname);
+ }
+ OMATCH_CASE_NONE ()
+ {
+ buf = STRPRINTF("%s-[%s]"_fmt, pos_str[num - 1], pos_str[10]);
+ }
+ }
+ OMATCH_END ();
+ }
+ else
+ {
+ buf = STRPRINTF("%s-[%s]"_fmt, pos_str[num - 1], pos_str[10]);
+ }
+ push_str<ScriptDataStr>(st->stack, buf);
+
+}
+
+/*==========================================
+ * 装備品による能力値ボーナス
+ *------------------------------------------
+ */
+static
+void builtin_bonus(ScriptState *st)
+{
+ SP type = SP(conv_num(st, &AARG(0)));
+ int val = conv_num(st, &AARG(1));
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ pc_bonus(sd, type, val);
+
+}
+
+/*==========================================
+ * 装備品による能力値ボーナス
+ *------------------------------------------
+ */
+static
+void builtin_bonus2(ScriptState *st)
+{
+ SP type = SP(conv_num(st, &AARG(0)));
+ int type2 = conv_num(st, &AARG(1));
+ int val = conv_num(st, &AARG(2));
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ pc_bonus2(sd, type, type2, val);
+
+}
+
+/*==========================================
+ * スキル所得
+ *------------------------------------------
+ */
+static
+void builtin_skill(ScriptState *st)
+{
+ int level, flag = 1;
+ dumb_ptr<map_session_data> sd;
+
+ SkillID id = SkillID(conv_num(st, &AARG(0)));
+ level = conv_num(st, &AARG(1));
+ if (HARG(2))
+ flag = conv_num(st, &AARG(2));
+ sd = script_rid2sd(st);
+ pc_skill(sd, id, level, flag);
+ clif_skillinfoblock(sd);
+
+}
+
+/*==========================================
+ * [Fate] Sets the skill level permanently
+ *------------------------------------------
+ */
+static
+void builtin_setskill(ScriptState *st)
+{
+ int level;
+ dumb_ptr<map_session_data> sd;
+
+ SkillID id = static_cast<SkillID>(conv_num(st, &AARG(0)));
+ level = conv_num(st, &AARG(1));
+ sd = script_rid2sd(st);
+
+ level = std::min(level, MAX_SKILL_LEVEL);
+ level = std::max(level, 0);
+ sd->status.skill[id].lv = level;
+ clif_skillinfoblock(sd);
+}
+
+/*==========================================
+ * スキルレベル所得
+ *------------------------------------------
+ */
+static
+void builtin_getskilllv(ScriptState *st)
+{
+ SkillID id = SkillID(conv_num(st, &AARG(0)));
+ push_int<ScriptDataInt>(st->stack, pc_checkskill(script_rid2sd(st), id));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_getgmlevel(ScriptState *st)
+{
+ push_int<ScriptDataInt>(st->stack, pc_isGM(script_rid2sd(st)).get_all_bits());
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_end(ScriptState *st)
+{
+ st->state = ScriptEndState::END;
+}
+
+/*==========================================
+ * [Freeyorp] Return the current opt2
+ *------------------------------------------
+ */
+
+static
+void builtin_getopt2(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd;
+
+ sd = script_rid2sd(st);
+
+ push_int<ScriptDataInt>(st->stack, static_cast<uint16_t>(sd->opt2));
+
+}
+
+/*==========================================
+ * [Freeyorp] Sets opt2
+ *------------------------------------------
+ */
+
+static
+void builtin_setopt2(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd;
+
+ Opt2 new_opt2 = Opt2(conv_num(st, &AARG(0)));
+ sd = script_rid2sd(st);
+ if (new_opt2 == sd->opt2)
+ return;
+ sd->opt2 = new_opt2;
+ clif_changeoption(sd);
+ pc_calcstatus(sd, 0);
+
+}
+
+/*==========================================
+ * セーブポイントの保存
+ *------------------------------------------
+ */
+static
+void builtin_savepoint(ScriptState *st)
+{
+ int x, y;
+
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ x = conv_num(st, &AARG(1));
+ y = conv_num(st, &AARG(2));
+ pc_setsavepoint(script_rid2sd(st), str, x, y);
+}
+
+/*==========================================
+ * gettimetick(type)
+ *
+ * type The type of time measurement.
+ * Specify 0 for the system tick, 1 for
+ * seconds elapsed today, or 2 for seconds
+ * since Unix epoch. Defaults to 0 for any
+ * other value.
+ *------------------------------------------
+ */
+static
+void builtin_gettimetick(ScriptState *st) /* Asgard Version */
+{
+ int type;
+ type = conv_num(st, &AARG(0));
+
+ switch (type)
+ {
+ /* Number of seconds elapsed today(0-86399, 00:00:00-23:59:59). */
+ case 1:
+ {
+ struct tm t = TimeT::now();
+ push_int<ScriptDataInt>(st->stack,
+ t.tm_hour * 3600 + t.tm_min * 60 + t.tm_sec);
+ break;
+ }
+ /* Seconds since Unix epoch. */
+ case 2:
+ push_int<ScriptDataInt>(st->stack, static_cast<time_t>(TimeT::now()));
+ break;
+ /* System tick(unsigned int, and yes, it will wrap). */
+ case 0:
+ default:
+ push_int<ScriptDataInt>(st->stack, gettick().time_since_epoch().count());
+ break;
+ }
+}
+
+/*==========================================
+ * GetTime(Type);
+ * 1: Sec 2: Min 3: Hour
+ * 4: WeekDay 5: MonthDay 6: Month
+ * 7: Year
+ *------------------------------------------
+ */
+static
+void builtin_gettime(ScriptState *st) /* Asgard Version */
+{
+ int type = conv_num(st, &AARG(0));
+
+ struct tm t = TimeT::now();
+
+ switch (type)
+ {
+ case 1: //Sec(0~59)
+ push_int<ScriptDataInt>(st->stack, t.tm_sec);
+ break;
+ case 2: //Min(0~59)
+ push_int<ScriptDataInt>(st->stack, t.tm_min);
+ break;
+ case 3: //Hour(0~23)
+ push_int<ScriptDataInt>(st->stack, t.tm_hour);
+ break;
+ case 4: //WeekDay(0~6)
+ push_int<ScriptDataInt>(st->stack, t.tm_wday);
+ break;
+ case 5: //MonthDay(01~31)
+ push_int<ScriptDataInt>(st->stack, t.tm_mday);
+ break;
+ case 6: //Month(01~12)
+ push_int<ScriptDataInt>(st->stack, t.tm_mon + 1);
+ break;
+ case 7: //Year(20xx)
+ push_int<ScriptDataInt>(st->stack, t.tm_year + 1900);
+ break;
+ default: //(format error)
+ push_int<ScriptDataInt>(st->stack, -1);
+ break;
+ }
+}
+
+/*==========================================
+ * カプラ倉庫を開く
+ *------------------------------------------
+ */
+static
+void builtin_openstorage(ScriptState *st)
+{
+// int sync = 0;
+// if (st->end >= 3) sync = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+// if (sync) {
+ st->state = ScriptEndState::STOP;
+ sd->npc_flags.storage = 1;
+// } else st->state = ScriptEndState::END;
+
+ storage_storageopen(sd);
+}
+
+/*==========================================
+ * NPCで経験値上げる
+ *------------------------------------------
+ */
+static
+void builtin_getexp(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ int base = 0, job = 0;
+
+ base = conv_num(st, &AARG(0));
+ job = conv_num(st, &AARG(1));
+ if (base < 0 || job < 0)
+ return;
+ if (sd)
+ pc_gainexp_reason(sd, base, job, PC_GAINEXP_REASON::SCRIPT);
+
+}
+
+/*==========================================
+ * モンスター発生
+ *------------------------------------------
+ */
+static
+void builtin_monster(ScriptState *st)
+{
+ Species mob_class;
+ int amount, x, y;
+ NpcEvent event;
+
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ x = conv_num(st, &AARG(1));
+ y = conv_num(st, &AARG(2));
+ MobName str = stringish<MobName>(ZString(conv_str(st, &AARG(3))));
+ mob_class = wrap<Species>(conv_num(st, &AARG(4)));
+ amount = conv_num(st, &AARG(5));
+ if (HARG(6))
+ extract(ZString(conv_str(st, &AARG(6))), &event);
+
+ mob_once_spawn(map_id2sd(st->rid), mapname, x, y, str, mob_class, amount,
+ event);
+}
+
+/*==========================================
+ * モンスター発生
+ *------------------------------------------
+ */
+static
+void builtin_areamonster(ScriptState *st)
+{
+ Species mob_class;
+ int amount, x0, y0, x1, y1;
+ NpcEvent event;
+
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ x0 = conv_num(st, &AARG(1));
+ y0 = conv_num(st, &AARG(2));
+ x1 = conv_num(st, &AARG(3));
+ y1 = conv_num(st, &AARG(4));
+ MobName str = stringish<MobName>(ZString(conv_str(st, &AARG(5))));
+ mob_class = wrap<Species>(conv_num(st, &AARG(6)));
+ amount = conv_num(st, &AARG(7));
+ if (HARG(8))
+ extract(ZString(conv_str(st, &AARG(8))), &event);
+
+ mob_once_spawn_area(map_id2sd(st->rid), mapname, x0, y0, x1, y1, str, mob_class,
+ amount, event);
+}
+
+/*==========================================
+ * モンスター削除
+ *------------------------------------------
+ */
+static
+void builtin_killmonster_sub(dumb_ptr<block_list> bl, NpcEvent event)
+{
+ dumb_ptr<mob_data> md = bl->is_mob();
+ if (event)
+ {
+ if (event == md->npc_event)
+ mob_delete(md);
+ return;
+ }
+ else if (!event)
+ {
+ if (md->spawn.delay1 == static_cast<interval_t>(-1)
+ && md->spawn.delay2 == static_cast<interval_t>(-1))
+ mob_delete(md);
+ return;
+ }
+}
+
+static
+void builtin_killmonster(ScriptState *st)
+{
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ ZString event_ = ZString(conv_str(st, &AARG(1)));
+ NpcEvent event;
+ if (event_ != "All"_s)
+ extract(event_, &event);
+
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
+ map_foreachinarea(std::bind(builtin_killmonster_sub, ph::_1, event),
+ m,
+ 0, 0,
+ m->xs, m->ys,
+ BL::MOB);
+}
+
+/*==========================================
+ * NPC主体イベント実行
+ *------------------------------------------
+ */
+static
+void builtin_donpcevent(ScriptState *st)
+{
+ ZString event_ = ZString(conv_str(st, &AARG(0)));
+ NpcEvent event;
+ extract(event_, &event);
+ npc_event_do(event);
+}
+
+/*==========================================
+ * イベントタイマー追加
+ *------------------------------------------
+ */
+static
+void builtin_addtimer(ScriptState *st)
+{
+ interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(0)));
+ ZString event_ = ZString(conv_str(st, &AARG(1)));
+ NpcEvent event;
+ extract(event_, &event);
+ pc_addeventtimer(script_rid2sd(st), tick, event);
+}
+
+/*==========================================
+ * NPCタイマー初期化
+ *------------------------------------------
+ */
+static
+void builtin_initnpctimer(ScriptState *st)
+{
+ dumb_ptr<npc_data> nd_;
+ if (HARG(0))
+ nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0)))));
+ else
+ nd_ = map_id_is_npc(st->oid);
+ assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
+ dumb_ptr<npc_data_script> nd = nd_->is_script();
+
+ npc_settimerevent_tick(nd, interval_t::zero());
+ npc_timerevent_start(nd);
+}
+
+/*==========================================
+ * NPCタイマー開始
+ *------------------------------------------
+ */
+static
+void builtin_startnpctimer(ScriptState *st)
+{
+ dumb_ptr<npc_data> nd_;
+ if (HARG(0))
+ nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0)))));
+ else
+ nd_ = map_id_is_npc(st->oid);
+ assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
+ dumb_ptr<npc_data_script> nd = nd_->is_script();
+
+ npc_timerevent_start(nd);
+}
+
+/*==========================================
+ * NPCタイマー停止
+ *------------------------------------------
+ */
+static
+void builtin_stopnpctimer(ScriptState *st)
+{
+ dumb_ptr<npc_data> nd_;
+ if (HARG(0))
+ nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0)))));
+ else
+ nd_ = map_id_is_npc(st->oid);
+ assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
+ dumb_ptr<npc_data_script> nd = nd_->is_script();
+
+ npc_timerevent_stop(nd);
+}
+
+/*==========================================
+ * NPCタイマー情報所得
+ *------------------------------------------
+ */
+static
+void builtin_getnpctimer(ScriptState *st)
+{
+ dumb_ptr<npc_data> nd_;
+ int type = conv_num(st, &AARG(0));
+ int val = 0;
+ if (HARG(1))
+ nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(1)))));
+ else
+ nd_ = map_id_is_npc(st->oid);
+ assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
+ dumb_ptr<npc_data_script> nd = nd_->is_script();
+
+ switch (type)
+ {
+ case 0:
+ val = npc_gettimerevent_tick(nd).count();
+ break;
+ case 1:
+ val = nd->scr.timer_active;
+ break;
+ case 2:
+ val = nd->scr.timer_eventv.size();
+ break;
+ }
+ push_int<ScriptDataInt>(st->stack, val);
+}
+
+/*==========================================
+ * NPCタイマー値設定
+ *------------------------------------------
+ */
+static
+void builtin_setnpctimer(ScriptState *st)
+{
+ dumb_ptr<npc_data> nd_;
+ interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(0)));
+ if (HARG(1))
+ nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(1)))));
+ else
+ nd_ = map_id_is_npc(st->oid);
+ assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
+ dumb_ptr<npc_data_script> nd = nd_->is_script();
+
+ npc_settimerevent_tick(nd, tick);
+}
+
+/*==========================================
+ * 天の声アナウンス
+ *------------------------------------------
+ */
+static
+void builtin_announce(ScriptState *st)
+{
+ int flag;
+ ZString str = ZString(conv_str(st, &AARG(0)));
+ flag = conv_num(st, &AARG(1));
+
+ if (flag & 0x0f)
+ {
+ dumb_ptr<block_list> bl;
+ if (flag & 0x08)
+ bl = map_id2bl(st->oid);
+ else
+ bl = script_rid2sd(st);
+ clif_GMmessage(bl, str, flag);
+ }
+ else
+ intif_GMmessage(str);
+}
+
+/*==========================================
+ * 天の声アナウンス(特定マップ)
+ *------------------------------------------
+ */
+static
+void builtin_mapannounce_sub(dumb_ptr<block_list> bl, XString str, int flag)
+{
+ clif_GMmessage(bl, str, flag | 3);
+}
+
+static
+void builtin_mapannounce(ScriptState *st)
+{
+ int flag;
+
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ ZString str = ZString(conv_str(st, &AARG(1)));
+ flag = conv_num(st, &AARG(2));
+
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
+ map_foreachinarea(std::bind(builtin_mapannounce_sub, ph::_1, str, flag & 0x10),
+ m,
+ 0, 0,
+ m->xs, m->ys,
+ BL::PC);
+}
+
+/*==========================================
+ * ユーザー数所得
+ *------------------------------------------
+ */
+static
+void builtin_getusers(ScriptState *st)
+{
+ int flag = conv_num(st, &AARG(0));
+ dumb_ptr<block_list> bl = map_id2bl((flag & 0x08) ? st->oid : st->rid);
+ int val = 0;
+ switch (flag & 0x07)
+ {
+ case 0:
+ val = bl->bl_m->users;
+ break;
+ case 1:
+ val = map_getusers();
+ break;
+ }
+ push_int<ScriptDataInt>(st->stack, val);
+}
+
+/*==========================================
+ * マップ指定ユーザー数所得
+ *------------------------------------------
+ */
+static
+void builtin_getmapusers(ScriptState *st)
+{
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str),
+ {
+ push_int<ScriptDataInt>(st->stack, -1);
+ return;
+ });
+ push_int<ScriptDataInt>(st->stack, m->users);
+}
+
+/*==========================================
+ * エリア指定ユーザー数所得
+ *------------------------------------------
+ */
+static
+void builtin_getareausers_sub(dumb_ptr<block_list> bl, int *users)
+{
+ if (bool(bl->is_player()->status.option & Opt0::HIDE))
+ return;
+ (*users)++;
+}
+
+static
+void builtin_getareausers_living_sub(dumb_ptr<block_list> bl, int *users)
+{
+ if (bool(bl->is_player()->status.option & Opt0::HIDE))
+ return;
+ if (!pc_isdead(bl->is_player()))
+ (*users)++;
+}
+
+static
+void builtin_getareausers(ScriptState *st)
+{
+ int x0, y0, x1, y1, users = 0;
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ x0 = conv_num(st, &AARG(1));
+ y0 = conv_num(st, &AARG(2));
+ x1 = conv_num(st, &AARG(3));
+ y1 = conv_num(st, &AARG(4));
+
+ int living = 0;
+ if (HARG(5))
+ {
+ living = conv_num(st, &AARG(5));
+ }
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str),
+ {
+ push_int<ScriptDataInt>(st->stack, -1);
+ return;
+ });
+ map_foreachinarea(std::bind(living ? builtin_getareausers_living_sub: builtin_getareausers_sub, ph::_1, &users),
+ m,
+ x0, y0,
+ x1, y1,
+ BL::PC);
+ push_int<ScriptDataInt>(st->stack, users);
+}
+
+/*==========================================
+ * エリア指定ドロップアイテム数所得
+ *------------------------------------------
+ */
+static
+void builtin_getareadropitem_sub(dumb_ptr<block_list> bl, ItemNameId item, int *amount)
+{
+ dumb_ptr<flooritem_data> drop = bl->is_item();
+
+ if (drop->item_data.nameid == item)
+ (*amount) += drop->item_data.amount;
+
+}
+
+static
+void builtin_getareadropitem_sub_anddelete(dumb_ptr<block_list> bl, ItemNameId item, int *amount)
+{
+ dumb_ptr<flooritem_data> drop = bl->is_item();
+
+ if (drop->item_data.nameid == item)
+ {
+ (*amount) += drop->item_data.amount;
+ clif_clearflooritem(drop, nullptr);
+ map_delobject(drop->bl_id, drop->bl_type);
+ }
+}
+
+static
+void builtin_getareadropitem(ScriptState *st)
+{
+ ItemNameId item;
+ int x0, y0, x1, y1, amount = 0, delitems = 0;
+ struct script_data *data;
+
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ x0 = conv_num(st, &AARG(1));
+ y0 = conv_num(st, &AARG(2));
+ x1 = conv_num(st, &AARG(3));
+ y1 = conv_num(st, &AARG(4));
+
+ data = &AARG(5);
+ get_val(st, data);
+ if (data->is<ScriptDataStr>())
+ {
+ ZString name = ZString(conv_str(st, data));
+ Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
+ OMATCH_BEGIN_SOME (item_data, item_data_)
+ {
+ item = item_data->nameid;
+ }
+ OMATCH_END ();
+ }
+ else
+ item = wrap<ItemNameId>(conv_num(st, data));
+
+ if (HARG(6))
+ delitems = conv_num(st, &AARG(6));
+
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str),
+ {
+ push_int<ScriptDataInt>(st->stack, -1);
+ return;
+ });
+ if (delitems)
+ map_foreachinarea(std::bind(builtin_getareadropitem_sub_anddelete, ph::_1, item, &amount),
+ m,
+ x0, y0,
+ x1, y1,
+ BL::ITEM);
+ else
+ map_foreachinarea(std::bind(builtin_getareadropitem_sub, ph::_1, item, &amount),
+ m,
+ x0, y0,
+ x1, y1,
+ BL::ITEM);
+
+ push_int<ScriptDataInt>(st->stack, amount);
+}
+
+/*==========================================
+ * NPCの有効化
+ *------------------------------------------
+ */
+static
+void builtin_enablenpc(ScriptState *st)
+{
+ NpcName str = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
+ npc_enable(str, 1);
+}
+
+/*==========================================
+ * NPCの無効化
+ *------------------------------------------
+ */
+static
+void builtin_disablenpc(ScriptState *st)
+{
+ NpcName str = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
+ npc_enable(str, 0);
+}
+
+/*==========================================
+ * 状態異常にかかる
+ *------------------------------------------
+ */
+static
+void builtin_sc_start(ScriptState *st)
+{
+ dumb_ptr<block_list> bl;
+ int val1;
+ StatusChange type = static_cast<StatusChange>(conv_num(st, &AARG(0)));
+ interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(1)));
+ if (tick < 1_s)
+ // work around old behaviour of:
+ // speed potion
+ // atk potion
+ // matk potion
+ //
+ // which used to use seconds
+ // all others used milliseconds
+ tick *= 1000;
+ val1 = conv_num(st, &AARG(2));
+ if (HARG(3)) //指定したキャラを状態異常にする
+ bl = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(3))));
+ else
+ bl = map_id2bl(st->rid);
+ skill_status_change_start(bl, type, val1, tick);
+}
+
+/*==========================================
+ * 状態異常が直る
+ *------------------------------------------
+ */
+static
+void builtin_sc_end(ScriptState *st)
+{
+ dumb_ptr<block_list> bl;
+ StatusChange type = StatusChange(conv_num(st, &AARG(0)));
+ bl = map_id2bl(st->rid);
+ skill_status_change_end(bl, type, nullptr);
+}
+
+static
+void builtin_sc_check(ScriptState *st)
+{
+ dumb_ptr<block_list> bl;
+ StatusChange type = StatusChange(conv_num(st, &AARG(0)));
+ bl = map_id2bl(st->rid);
+
+ push_int<ScriptDataInt>(st->stack, skill_status_change_active(bl, type));
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static
+void builtin_debugmes(ScriptState *st)
+{
+ RString mes = conv_str(st, &AARG(0));
+ PRINTF("script debug: %d %d: '%s'\n"_fmt,
+ st->rid, st->oid, mes);
+}
+
+/*==========================================
+ * ステータスリセット
+ *------------------------------------------
+ */
+static
+void builtin_resetstatus(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd;
+ sd = script_rid2sd(st);
+ pc_resetstate(sd);
+}
+
+/*==========================================
+ * RIDのアタッチ
+ *------------------------------------------
+ */
+static
+void builtin_attachrid(ScriptState *st)
+{
+ st->rid = wrap<BlockId>(conv_num(st, &AARG(0)));
+ push_int<ScriptDataInt>(st->stack, (map_id2sd(st->rid) != nullptr));
+}
+
+/*==========================================
+ * RIDのデタッチ
+ *------------------------------------------
+ */
+static
+void builtin_detachrid(ScriptState *st)
+{
+ st->rid = BlockId();
+}
+
+/*==========================================
+ * 存在チェック
+ *------------------------------------------
+ */
+static
+void builtin_isloggedin(ScriptState *st)
+{
+ push_int<ScriptDataInt>(st->stack,
+ map_id2sd(wrap<BlockId>(conv_num(st, &AARG(0)))) != nullptr);
+}
+
+static
+void builtin_setmapflag(ScriptState *st)
+{
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ int i = conv_num(st, &AARG(1));
+ MapFlag mf = map_flag_from_int(i);
+ Option<P<map_local>> m_ = map_mapname2mapid(str);
+ OMATCH_BEGIN_SOME (m, m_)
+ {
+ m->flag.set(mf, 1);
+ }
+ OMATCH_END ();
+}
+
+static
+void builtin_removemapflag(ScriptState *st)
+{
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ int i = conv_num(st, &AARG(1));
+ MapFlag mf = map_flag_from_int(i);
+ Option<P<map_local>> m_ = map_mapname2mapid(str);
+ OMATCH_BEGIN_SOME (m, m_)
+ {
+ m->flag.set(mf, 0);
+ }
+ OMATCH_END ();
+}
+
+static
+void builtin_getmapflag(ScriptState *st)
+{
+ int r = -1;
+
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ int i = conv_num(st, &AARG(1));
+ MapFlag mf = map_flag_from_int(i);
+ Option<P<map_local>> m_ = map_mapname2mapid(str);
+ OMATCH_BEGIN_SOME (m, m_)
+ {
+ r = m->flag.get(mf);
+ }
+ OMATCH_END ();
+
+ push_int<ScriptDataInt>(st->stack, r);
+}
+
+static
+void builtin_pvpon(ScriptState *st)
+{
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str), return);
+ if (!m->flag.get(MapFlag::PVP) && !m->flag.get(MapFlag::NOPVP))
+ {
+ m->flag.set(MapFlag::PVP, 1);
+
+ if (battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris]
+ return;
+
+ for (io::FD i : iter_fds())
+ {
+ Session *s = get_session(i);
+ if (!s)
+ continue;
+ map_session_data *pl_sd = static_cast<map_session_data *>(s->session_data.get());
+ if (pl_sd && pl_sd->state.auth)
+ {
+ if (m == pl_sd->bl_m && !pl_sd->pvp_timer)
+ {
+ pl_sd->pvp_timer = Timer(gettick() + 200_ms,
+ std::bind(pc_calc_pvprank_timer, ph::_1, ph::_2,
+ pl_sd->bl_id));
+ pl_sd->pvp_rank = 0;
+ pl_sd->pvp_point = 5;
+ }
+ }
+ }
+ }
+}
+
+static
+void builtin_pvpoff(ScriptState *st)
+{
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str), return);
+ if (m->flag.get(MapFlag::PVP) && m->flag.get(MapFlag::NOPVP))
+ {
+ m->flag.set(MapFlag::PVP, 0);
+
+ if (battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris]
+ return;
+
+ for (io::FD i : iter_fds())
+ {
+ Session *s = get_session(i);
+ if (!s)
+ continue;
+ map_session_data *pl_sd = static_cast<map_session_data *>(s->session_data.get());
+ if (pl_sd && pl_sd->state.auth)
+ {
+ if (m == pl_sd->bl_m)
+ {
+ pl_sd->pvp_timer.cancel();
+ }
+ }
+ }
+ }
+}
+
+static
+void builtin_setpvpchannel(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ int flag;
+ flag = conv_num(st, &AARG(0));
+ if (flag < 1)
+ flag = 0;
+
+ sd->state.pvpchannel = flag;
+}
+
+static
+void builtin_getpvpflag(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ int num = conv_num(st, &AARG(0));
+ int flag = 0;
+
+ switch (num){
+ case 0:
+ flag = sd->state.pvpchannel;
+ break;
+ case 1:
+ flag = bool(sd->status.option & Opt0::HIDE);
+ break;
+ }
+
+ push_int<ScriptDataInt>(st->stack, flag);
+}
+
+/*==========================================
+ * NPCエモーション
+ *------------------------------------------
+ */
+
+static
+void builtin_emotion(ScriptState *st)
+{
+ ZString str;
+ dumb_ptr<map_session_data> pl_sd = nullptr;
+ int type = conv_num(st, &AARG(0));
+ if (HARG(1)) {
+ str = ZString(conv_str(st, &AARG(1)));
+ CharName player = stringish<CharName>(str);
+ pl_sd = map_nick2sd(player);
+ }
+ if (type < 0 || type > 200)
+ return;
+ if (pl_sd != nullptr)
+ clif_emotion_towards(map_id2bl(st->oid), pl_sd, type);
+ else if (st->rid && str == "self"_s)
+ clif_emotion(map_id2sd(st->rid), type);
+ else
+ clif_emotion(map_id2bl(st->oid), type);
+}
+
+static
+void builtin_mapwarp(ScriptState *st) // Added by RoVeRT
+{
+ int x, y;
+ int x0, y0, x1, y1;
+
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ x0 = 0;
+ y0 = 0;
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
+ x1 = m->xs;
+ y1 = m->ys;
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(1))));
+ x = conv_num(st, &AARG(2));
+ y = conv_num(st, &AARG(3));
+
+ map_foreachinarea(std::bind(builtin_areawarp_sub, ph::_1, str, x, y),
+ m,
+ x0, y0,
+ x1, y1,
+ BL::PC);
+}
+
+static
+void builtin_mobcount_sub(dumb_ptr<block_list> bl, NpcEvent event, int *c)
+{
+ if (event == bl->is_mob()->npc_event)
+ (*c)++;
+}
+
+static
+void builtin_mobcount(ScriptState *st) // Added by RoVeRT
+{
+ int c = 0;
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ ZString event_ = ZString(conv_str(st, &AARG(1)));
+ NpcEvent event;
+ extract(event_, &event);
+
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname),
+ {
+ push_int<ScriptDataInt>(st->stack, -1);
+ return;
+ });
+ map_foreachinarea(std::bind(builtin_mobcount_sub, ph::_1, event, &c),
+ m,
+ 0, 0,
+ m->xs, m->ys,
+ BL::MOB);
+
+ push_int<ScriptDataInt>(st->stack, (c - 1));
+
+}
+
+static
+void builtin_marriage(ScriptState *st)
+{
+ CharName partner = stringish<CharName>(ZString(conv_str(st, &AARG(0))));
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ dumb_ptr<map_session_data> p_sd = map_nick2sd(partner);
+
+ if (sd == nullptr || p_sd == nullptr || pc_marriage(sd, p_sd) < 0)
+ {
+ push_int<ScriptDataInt>(st->stack, 0);
+ return;
+ }
+ push_int<ScriptDataInt>(st->stack, 1);
+}
+
+static
+void builtin_divorce(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ if (sd == nullptr || pc_divorce(sd) < 0)
+ {
+ push_int<ScriptDataInt>(st->stack, 0);
+ return;
+ }
+
+ push_int<ScriptDataInt>(st->stack, 1);
+}
+
+/*==========================================
+ * IDからItem名
+ *------------------------------------------
+ */
+static
+void builtin_getitemname(ScriptState *st)
+{
+ Option<P<struct item_data>> i_data = None;
+ struct script_data *data;
+
+ data = &AARG(0);
+ get_val(st, data);
+ if (data->is<ScriptDataStr>())
+ {
+ ZString name = ZString(conv_str(st, data));
+ i_data = itemdb_searchname(name);
+ }
+ else
+ {
+ ItemNameId item_id = wrap<ItemNameId>(conv_num(st, data));
+ i_data = Some(itemdb_search(item_id));
+ }
+
+ RString item_name = i_data.pmd_pget(&item_data::jname).copy_or(stringish<ItemName>("Unknown Item"_s));
+
+ push_str<ScriptDataStr>(st->stack, item_name);
+}
+
+static
+void builtin_getitemlink(ScriptState *st)
+{
+ struct script_data *data;
+ AString buf;
+ data = &AARG(0);
+ ZString name = conv_str(st, data);
+
+ ItemNameId item_id;
+ Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
+ OMATCH_BEGIN (item_data_)
+ {
+ OMATCH_CASE_SOME (item_data)
+ {
+ buf = STRPRINTF("@@%d|@@"_fmt, item_data->nameid);
+ }
+ OMATCH_CASE_NONE ()
+ {
+ buf = "Unknown Item"_s;
+ }
+ }
+ OMATCH_END ();
+ push_str<ScriptDataStr>(st->stack, buf);
+}
+
+static
+void builtin_getspellinvocation(ScriptState *st)
+{
+ RString name = conv_str(st, &AARG(0));
+
+ AString invocation = magic::magic_find_invocation(name);
+ if (!invocation)
+ invocation = "..."_s;
+
+ push_str<ScriptDataStr>(st->stack, invocation);
+}
+
+static
+void builtin_getpartnerid2(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status.partner_id));
+}
+
+/*==========================================
+ * PCの所持品情報読み取り
+ *------------------------------------------
+ */
+static
+void builtin_getinventorylist(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ int j = 0;
+ if (!sd)
+ return;
+ for (IOff0 i : IOff0::iter())
+ {
+ if (sd->status.inventory[i].nameid
+ && sd->status.inventory[i].amount > 0)
+ {
+ pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_id"_s), j),
+ unwrap<ItemNameId>(sd->status.inventory[i].nameid));
+ pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_amount"_s), j),
+ sd->status.inventory[i].amount);
+ pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_equip"_s), j),
+ static_cast<uint16_t>(sd->status.inventory[i].equip));
+ j++;
+ }
+ }
+ pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_count"_s)), j);
+}
+
+static
+void builtin_getactivatedpoolskilllist(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ SkillID pool_skills[MAX_SKILL_POOL];
+ int skill_pool_size = skill_pool(sd, pool_skills);
+ int i, count = 0;
+
+ if (!sd)
+ return;
+
+ for (i = 0; i < skill_pool_size; i++)
+ {
+ SkillID skill_id = pool_skills[i];
+
+ if (sd->status.skill[skill_id].lv)
+ {
+ pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_id"_s), count),
+ static_cast<uint16_t>(skill_id));
+ pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_lv"_s), count),
+ sd->status.skill[skill_id].lv);
+ pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_flag"_s), count),
+ static_cast<uint16_t>(sd->status.skill[skill_id].flags));
+ pc_setregstr(sd, SIR::from(variable_names.intern("@skilllist_name$"_s), count),
+ skill_name(skill_id));
+ ++count;
+ }
+ }
+ pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_count"_s)), count);
+
+}
+
+static
+void builtin_getunactivatedpoolskilllist(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ int i, count = 0;
+
+ if (!sd)
+ return;
+
+ for (i = 0; i < skill_pool_skills.size(); i++)
+ {
+ SkillID skill_id = skill_pool_skills[i];
+
+ if (sd->status.skill[skill_id].lv
+ && !bool(sd->status.skill[skill_id].flags & SkillFlags::POOL_ACTIVATED))
+ {
+ pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_id"_s), count),
+ static_cast<uint16_t>(skill_id));
+ pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_lv"_s), count),
+ sd->status.skill[skill_id].lv);
+ pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_flag"_s), count),
+ static_cast<uint16_t>(sd->status.skill[skill_id].flags));
+ pc_setregstr(sd, SIR::from(variable_names.intern("@skilllist_name$"_s), count),
+ skill_name(skill_id));
+ ++count;
+ }
+ }
+ pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_count"_s)), count);
+}
+
+static
+void builtin_poolskill(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ SkillID skill_id = SkillID(conv_num(st, &AARG(0)));
+
+ skill_pool_activate(sd, skill_id);
+ clif_skillinfoblock(sd);
+
+}
+
+static
+void builtin_unpoolskill(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ SkillID skill_id = SkillID(conv_num(st, &AARG(0)));
+
+ skill_pool_deactivate(sd, skill_id);
+ clif_skillinfoblock(sd);
+
+}
+
+/*==========================================
+ * NPCから発生するエフェクト
+ * misceffect(effect, [target])
+ *
+ * effect The effect type/ID.
+ * target The player name or being ID on
+ * which to display the effect. If not
+ * specified, it attempts to default to
+ * the current NPC or invoking PC.
+ *------------------------------------------
+ */
+static
+void builtin_misceffect(ScriptState *st)
+{
+ int type;
+ BlockId id;
+ CharName name;
+ dumb_ptr<block_list> bl = nullptr;
+
+ type = conv_num(st, &AARG(0));
+
+ if (HARG(1))
+ {
+ struct script_data *sdata = &AARG(1);
+
+ get_val(st, sdata);
+
+ if (sdata->is<ScriptDataStr>())
+ name = stringish<CharName>(ZString(conv_str(st, sdata)));
+ else
+ id = wrap<BlockId>(conv_num(st, sdata));
+ }
+
+ if (name.to__actual())
+ {
+ dumb_ptr<map_session_data> sd = map_nick2sd(name);
+ if (sd)
+ bl = sd;
+ }
+ else if (id)
+ bl = map_id2bl(id);
+ else if (st->oid)
+ bl = map_id2bl(st->oid);
+ else
+ {
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ if (sd)
+ bl = sd;
+ }
+
+ if (bl)
+ clif_misceffect(bl, type);
+
+}
+
+/*==========================================
+ * Special effects [Valaris]
+ *------------------------------------------
+ */
+static
+void builtin_specialeffect(ScriptState *st)
+{
+ dumb_ptr<block_list> bl = map_id2bl(st->oid);
+
+ if (bl == nullptr)
+ return;
+
+ clif_specialeffect(bl,
+ conv_num(st,
+ &AARG(0)),
+ 0);
+
+}
+
+static
+void builtin_specialeffect2(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ if (sd == nullptr)
+ return;
+
+ clif_specialeffect(sd,
+ conv_num(st,
+ &AARG(0)),
+ 0);
+
+}
+
+/*==========================================
+ * Nude [Valaris]
+ *------------------------------------------
+ */
+
+static
+void builtin_nude(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ if (sd == nullptr)
+ return;
+
+ for (EQUIP i : EQUIPs)
+ {
+ IOff0 idx = sd->equip_index_maybe[i];
+ if (idx.ok())
+ pc_unequipitem(sd, idx, CalcStatus::LATER);
+ }
+ pc_calcstatus(sd, 0);
+
+}
+
+/*==========================================
+ * UnequipById [Freeyorp]
+ *------------------------------------------
+ */
+
+static
+void builtin_unequipbyid(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ if (sd == nullptr)
+ return;
+
+ EQUIP slot_id = EQUIP(conv_num(st, &AARG(0)));
+
+ if (slot_id >= EQUIP() && slot_id < EQUIP::COUNT)
+ {
+ IOff0 idx = sd->equip_index_maybe[slot_id];
+ if (idx.ok())
+ pc_unequipitem(sd, idx, CalcStatus::LATER);
+ }
+
+ pc_calcstatus(sd, 0);
+
+}
+
+/*==========================================
+ * npcwarp [remoitnane]
+ * Move NPC to a new position on the same map.
+ *------------------------------------------
+ */
+static
+void builtin_npcwarp(ScriptState *st)
+{
+ int x, y;
+ dumb_ptr<npc_data> nd = nullptr;
+
+ x = conv_num(st, &AARG(0));
+ y = conv_num(st, &AARG(1));
+ NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARG(2))));
+ nd = npc_name2id(npc);
+
+ if (!nd)
+ {
+ PRINTF("builtin_npcwarp: no such npc: '%s'\n"_fmt, npc);
+ return;
+ }
+
+ P<map_local> m = nd->bl_m;
+
+ /* Crude sanity checks. */
+ if (!nd->bl_prev
+ || x < 0 || x > m->xs -1
+ || y < 0 || y > m->ys - 1)
+ return;
+
+ npc_enable(npc, 0);
+ map_delblock(nd); /* [Freeyorp] */
+ nd->bl_x = x;
+ nd->bl_y = y;
+ map_addblock(nd);
+ npc_enable(npc, 1);
+
+}
+
+/*==========================================
+ * npcareawarp [remoitnane] [wushin]
+ * Move NPC to a new area on the same map.
+ *------------------------------------------
+ */
+static
+void builtin_npcareawarp(ScriptState *st)
+{
+ int x0, y0, x1, y1, x, y, max, cb, lx = -1, ly = -1, j = 0;
+ dumb_ptr<npc_data> nd = nullptr;
+
+ NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARG(5))));
+ nd = npc_name2id(npc);
+
+ x0 = conv_num(st, &AARG(0));
+ y0 = conv_num(st, &AARG(1));
+ x1 = conv_num(st, &AARG(2));
+ y1 = conv_num(st, &AARG(3));
+ cb = conv_num(st, &AARG(4));
+
+ if (!nd)
+ {
+ PRINTF("builtin_npcareawarp: no such npc: '%s'\n"_fmt, npc);
+ return;
+ }
+
+ max = (y1 - y0 + 1) * (x1 - x0 + 1) * 3;
+ if (max > 1000)
+ max = 1000;
+
+ P<map_local> m = nd->bl_m;
+ if (cb) {
+ do
+ {
+ x = random_::in(x0, x1);
+ y = random_::in(y0, y1);
+ }
+ while (bool(map_getcell(m, x, y) & MapCell::UNWALKABLE)
+ && (++j) < max);
+ if (j >= max)
+ {
+ if (lx >= 0)
+ { // Since reference went wrong, the place which boiled before is used.
+ x = lx;
+ y = ly;
+ }
+ else
+ return; // Since reference of the place which boils first went wrong, it stops.
+ }
+ }
+ else
+ x = random_::in(x0, x1);
+ y = random_::in(y0, y1);
+
+ npc_enable(npc, 0);
+ map_delblock(nd); /* [Freeyorp] */
+ nd->bl_x = x;
+ nd->bl_y = y;
+ map_addblock(nd);
+ npc_enable(npc, 1);
+
+}
+
+/*==========================================
+ * message [MouseJstr]
+ *------------------------------------------
+ */
+
+static
+void builtin_message(ScriptState *st)
+{
+ CharName player = stringish<CharName>(ZString(conv_str(st, &AARG(0))));
+ ZString msg = ZString(conv_str(st, &AARG(1)));
+
+ dumb_ptr<map_session_data> pl_sd = map_nick2sd(player);
+ if (pl_sd == nullptr)
+ return;
+ clif_displaymessage(pl_sd->sess, msg);
+
+}
+
+/*==========================================
+ * npctalk (sends message to surrounding
+ * area) [Valaris]
+ *------------------------------------------
+ */
+
+static
+void builtin_npctalk(ScriptState *st)
+{
+ dumb_ptr<npc_data> nd = map_id_is_npc(st->oid);
+ RString str = conv_str(st, &AARG(0));
+
+ if (nd)
+ {
+ clif_message(nd, str);
+ }
+}
+
+/*==========================================
+ * getlook char info. getlook(arg)
+ *------------------------------------------
+ */
+static
+void builtin_getlook(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ LOOK type = LOOK(conv_num(st, &AARG(0)));
+ int val = -1;
+ switch (type)
+ {
+ case LOOK::HAIR: //1
+ val = sd->status.hair;
+ break;
+ case LOOK::WEAPON: //2
+ val = static_cast<uint16_t>(sd->status.weapon);
+ break;
+ case LOOK::HEAD_BOTTOM: //3
+ val = unwrap<ItemNameId>(sd->status.head_bottom);
+ break;
+ case LOOK::HEAD_TOP: //4
+ val = unwrap<ItemNameId>(sd->status.head_top);
+ break;
+ case LOOK::HEAD_MID: //5
+ val = unwrap<ItemNameId>(sd->status.head_mid);
+ break;
+ case LOOK::HAIR_COLOR: //6
+ val = sd->status.hair_color;
+ break;
+ case LOOK::CLOTHES_COLOR: //7
+ val = sd->status.clothes_color;
+ break;
+ case LOOK::SHIELD: //8
+ val = unwrap<ItemNameId>(sd->status.shield);
+ break;
+ case LOOK::SHOES: //9
+ break;
+ }
+
+ push_int<ScriptDataInt>(st->stack, val);
+}
+
+/*==========================================
+ * get char save point. argument: 0- map name, 1- x, 2- y
+ *------------------------------------------
+*/
+static
+void builtin_getsavepoint(ScriptState *st)
+{
+ int x, y, type;
+ dumb_ptr<map_session_data> sd;
+
+ sd = script_rid2sd(st);
+
+ type = conv_num(st, &AARG(0));
+
+ x = sd->status.save_point.x;
+ y = sd->status.save_point.y;
+ switch (type)
+ {
+ case 0:
+ {
+ RString mapname = sd->status.save_point.map_;
+ push_str<ScriptDataStr>(st->stack, mapname);
+ }
+ break;
+ case 1:
+ push_int<ScriptDataInt>(st->stack, x);
+ break;
+ case 2:
+ push_int<ScriptDataInt>(st->stack, y);
+ break;
+ }
+}
+
+/*==========================================
+ * areatimer
+ *------------------------------------------
+ */
+static
+void builtin_areatimer_sub(dumb_ptr<block_list> bl, interval_t tick, NpcEvent event)
+{
+ pc_addeventtimer(bl->is_player(), tick, event);
+}
+
+static
+void builtin_areatimer(ScriptState *st)
+{
+ int x0, y0, x1, y1;
+
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ x0 = conv_num(st, &AARG(1));
+ y0 = conv_num(st, &AARG(2));
+ x1 = conv_num(st, &AARG(3));
+ y1 = conv_num(st, &AARG(4));
+ interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(5)));
+ ZString event_ = ZString(conv_str(st, &AARG(6)));
+ NpcEvent event;
+ extract(event_, &event);
+
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
+
+ map_foreachinarea(std::bind(builtin_areatimer_sub, ph::_1, tick, event),
+ m,
+ x0, y0,
+ x1, y1,
+ BL::PC);
+}
+
+/*==========================================
+ * Check whether the PC is in the specified rectangle
+ *------------------------------------------
+ */
+static
+void builtin_isin(ScriptState *st)
+{
+ int x1, y1, x2, y2;
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ x1 = conv_num(st, &AARG(1));
+ y1 = conv_num(st, &AARG(2));
+ x2 = conv_num(st, &AARG(3));
+ y2 = conv_num(st, &AARG(4));
+
+ if (!sd)
+ return;
+
+ push_int<ScriptDataInt>(st->stack,
+ (sd->bl_x >= x1 && sd->bl_x <= x2)
+ && (sd->bl_y >= y1 && sd->bl_y <= y2)
+ && (str == sd->bl_m->name_));
+}
+
+/*==========================================
+ * Check whether the coords are collision
+ *------------------------------------------
+ */
+static
+void builtin_iscollision(ScriptState *st)
+{
+ int x, y;
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
+
+ x = conv_num(st, &AARG(1));
+ y = conv_num(st, &AARG(2));
+
+ push_int<ScriptDataInt>(st->stack,
+ bool(map_getcell(m, x, y) & MapCell::UNWALKABLE));
+}
+
+// Trigger the shop on a (hopefully) nearby shop NPC
+static
+void builtin_shop(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ dumb_ptr<npc_data> nd;
+
+ if (!sd)
+ return;
+
+ NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
+ nd = npc_name2id(name);
+ if (!nd)
+ {
+ PRINTF("builtin_shop: no such npc: '%s'\n"_fmt, name);
+ return;
+ }
+
+ builtin_close(st);
+ clif_npcbuysell(sd, nd->bl_id);
+}
+
+/*==========================================
+ * Check whether the PC is dead
+ *------------------------------------------
+ */
+static
+void builtin_isdead(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ push_int<ScriptDataInt>(st->stack, pc_isdead(sd));
+}
+
+/*========================================
+ * Changes a NPC name, and sprite
+ *----------------------------------------
+ */
+static
+void builtin_fakenpcname(ScriptState *st)
+{
+ NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
+ NpcName newname = stringish<NpcName>(ZString(conv_str(st, &AARG(1))));
+ Species newsprite = wrap<Species>(static_cast<uint16_t>(conv_num(st, &AARG(2))));
+ dumb_ptr<npc_data> nd = npc_name2id(name);
+ if (!nd)
+ {
+ PRINTF("builtin_fakenpcname: no such npc: '%s'\n"_fmt, name);
+ return;
+ }
+ nd->name = newname;
+ nd->npc_class = newsprite;
+
+ // Refresh this npc
+ npc_enable(name, 0);
+ npc_enable(name, 1);
+
+}
+
+/*============================
+ * Gets the PC's x pos
+ *----------------------------
+ */
+static
+void builtin_getx(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ push_int<ScriptDataInt>(st->stack, sd->bl_x);
+}
+
+/*============================
+ * Gets the PC's y pos
+ *----------------------------
+ */
+static
+void builtin_gety(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ push_int<ScriptDataInt>(st->stack, sd->bl_y);
+}
+
+/*
+ * Get the PC's current map's name
+ */
+static
+void builtin_getmap(ScriptState *st)
+{
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+
+ push_str<ScriptDataStr>(st->stack, sd->bl_m->name_);
+}
+
+/*
+ * Get the NPC's info
+ */
+static
+void builtin_strnpcinfo(ScriptState *st)
+{
+ int num = conv_num(st, &AARG(0));
+ RString name;
+ dumb_ptr<npc_data> nd;
+
+ if(HARG(1)){
+ NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARG(1))));
+ nd = npc_name2id(npc);
+ if (!nd)
+ {
+ PRINTF("builtin_strnpcinfo: no such npc: '%s'\n"_fmt, npc);
+ return;
+ }
+ } else {
+ nd = map_id_is_npc(st->oid);
+ }
+
+ switch(num)
+ {
+ case 0:
+ name = nd->name;
+ break;
+ case 1:
+ name = nd->name.xislice_h(std::find(nd->name.begin(), nd->name.end(), '#'));
+ break;
+ case 2:
+ name = nd->name.xislice_t(std::find(nd->name.begin(), nd->name.end(), '#'));
+ break;
+ case 3:
+ name = nd->bl_m->name_;
+ break;
+ }
+
+ push_str<ScriptDataStr>(st->stack, name);
+}
+
+/*============================
+ * Gets the NPC's x pos
+ *----------------------------
+ */
+static
+void builtin_getnpcx(ScriptState *st)
+{
+ dumb_ptr<npc_data> nd;
+
+ if(HARG(0)){
+ NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
+ nd = npc_name2id(name);
+ if (!nd)
+ {
+ PRINTF("builtin_getnpcx: no such npc: '%s'\n"_fmt, name);
+ return;
+ }
+ } else {
+ nd = map_id_is_npc(st->oid);
+ }
+
+ push_int<ScriptDataInt>(st->stack, nd->bl_x);
+}
+
+/*============================
+ * Gets the NPC's y pos
+ *----------------------------
+ */
+static
+void builtin_getnpcy(ScriptState *st)
+{
+ dumb_ptr<npc_data> nd;
+
+ if(HARG(0)){
+ NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
+ nd = npc_name2id(name);
+ if (!nd)
+ {
+ PRINTF("builtin_getnpcy: no such npc: '%s'\n"_fmt, name);
+ return;
+ }
+ } else {
+ nd = map_id_is_npc(st->oid);
+ }
+
+ push_int<ScriptDataInt>(st->stack, nd->bl_y);
+}
+
+static
+void builtin_mapexit(ScriptState *)
+{
+ runflag = 0;
+}
+
+
+#define BUILTIN(func, args, ret) \
+{builtin_##func, #func ## _s, args, ret}
+
+BuiltinFunction builtin_functions[] =
+{
+ BUILTIN(mes, "s"_s, '\0'),
+ BUILTIN(goto, "L"_s, '\0'),
+ BUILTIN(callfunc, "F"_s, '\0'),
+ BUILTIN(callsub, "L"_s, '\0'),
+ BUILTIN(return, ""_s, '\0'),
+ BUILTIN(next, ""_s, '\0'),
+ BUILTIN(close, ""_s, '\0'),
+ BUILTIN(close2, ""_s, '\0'),
+ BUILTIN(menu, "sL**"_s, '\0'),
+ BUILTIN(rand, "i?"_s, 'i'),
+ BUILTIN(isat, "Mxy"_s, 'i'),
+ BUILTIN(warp, "Mxy"_s, '\0'),
+ BUILTIN(areawarp, "MxyxyMxy"_s, '\0'),
+ BUILTIN(heal, "ii?"_s, '\0'),
+ BUILTIN(input, "N"_s, '\0'),
+ BUILTIN(if, "iF*"_s, '\0'),
+ BUILTIN(set, "Ne"_s, '\0'),
+ BUILTIN(setarray, "Ne*"_s, '\0'),
+ BUILTIN(cleararray, "Nei"_s, '\0'),
+ BUILTIN(getarraysize, "N"_s, 'i'),
+ BUILTIN(getelementofarray, "Ni"_s, '.'),
+ BUILTIN(setlook, "ii"_s, '\0'),
+ BUILTIN(countitem, "I"_s, 'i'),
+ BUILTIN(checkweight, "Ii"_s, 'i'),
+ BUILTIN(getitem, "Ii??"_s, '\0'),
+ BUILTIN(makeitem, "IiMxy"_s, '\0'),
+ BUILTIN(delitem, "Ii"_s, '\0'),
+ BUILTIN(getcharid, "i?"_s, 'i'),
+ BUILTIN(strcharinfo, "i"_s, 's'),
+ BUILTIN(getequipid, "i"_s, 'i'),
+ BUILTIN(getequipname, "i"_s, 's'),
+ BUILTIN(bonus, "ii"_s, '\0'),
+ BUILTIN(bonus2, "iii"_s, '\0'),
+ BUILTIN(skill, "ii?"_s, '\0'),
+ BUILTIN(setskill, "ii"_s, '\0'),
+ BUILTIN(getskilllv, "i"_s, 'i'),
+ BUILTIN(getgmlevel, ""_s, 'i'),
+ BUILTIN(end, ""_s, '\0'),
+ BUILTIN(getopt2, ""_s, 'i'),
+ BUILTIN(setopt2, "i"_s, '\0'),
+ BUILTIN(savepoint, "Mxy"_s, '\0'),
+ BUILTIN(gettimetick, "i"_s, 'i'),
+ BUILTIN(gettime, "i"_s, 'i'),
+ BUILTIN(openstorage, ""_s, '\0'),
+ BUILTIN(getexp, "ii"_s, '\0'),
+ BUILTIN(monster, "Mxysmi?"_s, '\0'),
+ BUILTIN(areamonster, "Mxyxysmi?"_s, '\0'),
+ BUILTIN(killmonster, "ME"_s, '\0'),
+ BUILTIN(donpcevent, "E"_s, '\0'),
+ BUILTIN(addtimer, "tE"_s, '\0'),
+ BUILTIN(initnpctimer, "?"_s, '\0'),
+ BUILTIN(startnpctimer, "?"_s, '\0'),
+ BUILTIN(stopnpctimer, "?"_s, '\0'),
+ BUILTIN(getnpctimer, "i?"_s, 'i'),
+ BUILTIN(setnpctimer, "i?"_s, '\0'),
+ BUILTIN(announce, "si"_s, '\0'),
+ BUILTIN(mapannounce, "Msi"_s, '\0'),
+ BUILTIN(getusers, "i"_s, 'i'),
+ BUILTIN(getmapusers, "M"_s, 'i'),
+ BUILTIN(getareausers, "Mxyxy?"_s, 'i'),
+ BUILTIN(getareadropitem, "Mxyxyi?"_s, 'i'),
+ BUILTIN(enablenpc, "s"_s, '\0'),
+ BUILTIN(disablenpc, "s"_s, '\0'),
+ BUILTIN(sc_start, "iTi?"_s, '\0'),
+ BUILTIN(sc_end, "i"_s, '\0'),
+ BUILTIN(sc_check, "i"_s, 'i'),
+ BUILTIN(debugmes, "s"_s, '\0'),
+ BUILTIN(wgm, "s"_s, '\0'),
+ BUILTIN(gmlog, "s"_s, '\0'),
+ BUILTIN(resetstatus, ""_s, '\0'),
+ BUILTIN(attachrid, "i"_s, 'i'),
+ BUILTIN(detachrid, ""_s, '\0'),
+ BUILTIN(isloggedin, "i"_s, 'i'),
+ BUILTIN(setmapflag, "Mi"_s, '\0'),
+ BUILTIN(removemapflag, "Mi"_s, '\0'),
+ BUILTIN(getmapflag, "Mi"_s, 'i'),
+ BUILTIN(pvpon, "M"_s, '\0'),
+ BUILTIN(pvpoff, "M"_s, '\0'),
+ BUILTIN(setpvpchannel, "i"_s, '\0'),
+ BUILTIN(getpvpflag, "i"_s, 'i'),
+ BUILTIN(emotion, "i?"_s, '\0'),
+ BUILTIN(mapwarp, "MMxy"_s, '\0'),
+ BUILTIN(mobcount, "ME"_s, 'i'),
+ BUILTIN(marriage, "P"_s, 'i'),
+ BUILTIN(divorce, ""_s, 'i'),
+ BUILTIN(getitemname, "I"_s, 's'),
+ BUILTIN(getitemlink, "I"_s, 's'),
+ BUILTIN(getspellinvocation, "s"_s, 's'),
+ BUILTIN(getpartnerid2, ""_s, 'i'),
+ BUILTIN(getinventorylist, ""_s, '\0'),
+ BUILTIN(getactivatedpoolskilllist, ""_s, '\0'),
+ BUILTIN(getunactivatedpoolskilllist, ""_s, '\0'),
+ BUILTIN(poolskill, "i"_s, '\0'),
+ BUILTIN(unpoolskill, "i"_s, '\0'),
+ BUILTIN(misceffect, "i?"_s, '\0'),
+ BUILTIN(specialeffect, "i"_s, '\0'),
+ BUILTIN(specialeffect2, "i"_s, '\0'),
+ BUILTIN(nude, ""_s, '\0'),
+ BUILTIN(unequipbyid, "i"_s, '\0'),
+ BUILTIN(npcwarp, "xys"_s, '\0'),
+ BUILTIN(npcareawarp, "xyxyis"_s, '\0'),
+ BUILTIN(message, "Ps"_s, '\0'),
+ BUILTIN(npctalk, "s"_s, '\0'),
+ BUILTIN(getlook, "i"_s, 'i'),
+ BUILTIN(getsavepoint, "i"_s, '.'),
+ BUILTIN(areatimer, "MxyxytE"_s, '\0'),
+ BUILTIN(isin, "Mxyxy"_s, 'i'),
+ BUILTIN(iscollision, "Mxy"_s, 'i'),
+ BUILTIN(shop, "s"_s, '\0'),
+ BUILTIN(isdead, ""_s, 'i'),
+ BUILTIN(fakenpcname, "ssi"_s, '\0'),
+ BUILTIN(getx, ""_s, 'i'),
+ BUILTIN(gety, ""_s, 'i'),
+ BUILTIN(getnpcx, "?"_s, 'i'),
+ BUILTIN(getnpcy, "?"_s, 'i'),
+ BUILTIN(strnpcinfo, "i?"_s, 's'),
+ BUILTIN(getmap, ""_s, 's'),
+ BUILTIN(mapexit, ""_s, '\0'),
+ BUILTIN(freeloop, "i"_s, '\0'),
+ {nullptr, ""_s, ""_s, '\0'},
+};
+} // namespace map
+} // namespace tmwa
diff --git a/src/sexpr/variant.cpp b/src/map/script-fun.hpp
index b1f500a..81d68fe 100644
--- a/src/sexpr/variant.cpp
+++ b/src/map/script-fun.hpp
@@ -1,7 +1,9 @@
-#include "variant.hpp"
-// variant.cpp - Just include the header file.
+#pragma once
+// script-fun.hpp - EAthena script frontend, engine, and library.
//
-// Copyright © 2012 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -18,23 +20,22 @@
// 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 "../poison.hpp"
+#include "fwd.hpp"
+#include "../strings/literal.hpp"
namespace tmwa
{
-namespace sexpr
+namespace map
{
-namespace
+struct BuiltinFunction
{
- struct Foo
- {
- Foo() {}
- ~Foo() {}
- Foo(Foo&&) {}
- Foo& operator = (Foo&&) { return *this; }
- };
-} // anonymous namespace
- static Variant<int, Foo> v;
-} // namespace sexpr
+ void (*func)(ScriptState *);
+ LString name;
+ LString arg;
+ char ret;
+};
+
+extern BuiltinFunction builtin_functions[];
+} // namespace map
} // namespace tmwa
diff --git a/src/map/script-parse-internal.hpp b/src/map/script-parse-internal.hpp
new file mode 100644
index 0000000..ddaeef0
--- /dev/null
+++ b/src/map/script-parse-internal.hpp
@@ -0,0 +1,67 @@
+#pragma once
+// script-parse-internal.hpp - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "script-parse.hpp"
+#include "fwd.hpp"
+
+#include "../strings/rstring.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+enum class StringCode : uint8_t
+{
+ NOP, POS, INT, PARAM, FUNC,
+ VARIABLE,
+};
+enum class ByteCode : uint8_t
+{
+ // types and specials
+ // Note that 'INT' is synthetic, and does not occur in the data stream
+ NOP, POS, INT, PARAM, FUNC, STR, ARG,
+ VARIABLE, EOL,
+
+ // unary and binary operators
+ LOR, LAND, LE, LT, GE, GT, EQ, NE,
+ XOR, OR, AND, ADD, SUB, MUL, DIV, MOD,
+ NEG, LNOT, NOT, R_SHIFT, L_SHIFT,
+
+ // additions
+ // needed because FUNC is used for the actual call
+ FUNC_REF,
+};
+
+struct str_data_t
+{
+ StringCode type;
+ RString strs;
+ int backpatch;
+ int label_;
+ int val;
+};
+
+Option<Borrowed<str_data_t>> search_strp(XString p);
+Borrowed<str_data_t> add_strp(XString p);
+} // namespace map
+} // namespace tmwa
diff --git a/src/map/script-parse.cpp b/src/map/script-parse.cpp
new file mode 100644
index 0000000..fb306c5
--- /dev/null
+++ b/src/map/script-parse.cpp
@@ -0,0 +1,851 @@
+#include "script-parse-internal.hpp"
+// script-parse.cpp - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011 Chuck Miller
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2013 wushin
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 <set>
+
+#include "../generic/array.hpp"
+#include "../generic/db.hpp"
+#include "../generic/intern-pool.hpp"
+
+#include "../strings/rstring.hpp"
+
+#include "../io/cxxstdio.hpp"
+
+#include "../mmo/cxxstdio_enums.hpp"
+
+#include "../ast/script.hpp"
+
+#include "globals.hpp"
+#include "map.t.hpp"
+#include "script-buffer.hpp"
+#include "script-call.hpp"
+#include "script-fun.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+constexpr bool DEBUG_DISP = false;
+
+class ScriptBuffer
+{
+ typedef ZString::iterator ZSit;
+
+ std::vector<ByteCode> script_buf;
+ RString debug_name;
+ std::vector<std::pair<ScriptLabel, size_t>> debug_labels;
+public:
+ ScriptBuffer(RString name) : debug_name(std::move(name)) {}
+
+ // construction methods
+ void add_scriptc(ByteCode a);
+ void add_scriptb(uint8_t a);
+ void add_scripti(uint32_t a);
+ void add_scriptl(Borrowed<str_data_t> a);
+ void set_label(Borrowed<str_data_t> ld, int pos_);
+ ZSit parse_simpleexpr(ZSit p);
+ ZSit parse_subexpr(ZSit p, int limit);
+ ZSit parse_expr(ZSit p);
+ ZSit parse_line(ZSit p, bool *canstep);
+ void parse_script(ZString src, int line, bool implicit_end);
+
+ // consumption methods
+ ByteCode operator[](size_t i) const { return script_buf[i]; }
+ ZString get_str(size_t i) const
+ {
+ return ZString(strings::really_construct_from_a_pointer, reinterpret_cast<const char *>(&script_buf[i]), nullptr);
+ }
+};
+} // namespace map
+} // namespace tmwa
+
+void std::default_delete<const tmwa::map::ScriptBuffer>::operator()(const tmwa::map::ScriptBuffer *sd)
+{
+ really_delete1 sd;
+}
+
+namespace tmwa
+{
+namespace map
+{
+// implemented for script-call.hpp because reasons
+ByteCode ScriptPointer::peek() const { return (*TRY_UNWRAP(code, abort()))[pos]; }
+ByteCode ScriptPointer::pop() { return (*TRY_UNWRAP(code, abort()))[pos++]; }
+ZString ScriptPointer::pops()
+{
+ ZString rv = TRY_UNWRAP(code, abort())->get_str(pos);
+ pos += rv.size();
+ ++pos;
+ return rv;
+}
+
+static
+struct ScriptConfigParse
+{
+ static const
+ int warn_func_no_comma = 1;
+ static const
+ int warn_cmd_no_comma = 1;
+ static const
+ int warn_func_mismatch_paramnum = 1;
+ static const
+ int warn_cmd_mismatch_paramnum = 1;
+} script_config;
+
+
+Option<Borrowed<str_data_t>> search_strp(XString p)
+{
+ return str_datam.search(p);
+}
+
+Borrowed<str_data_t> add_strp(XString p)
+{
+ Option<P<str_data_t>> rv_ = search_strp(p);
+ OMATCH_BEGIN_SOME (rv, rv_)
+ {
+ return rv;
+ }
+ OMATCH_END ();
+
+ RString p2 = p;
+ P<str_data_t> datum = str_datam.init(p2);
+ datum->type = StringCode::NOP;
+ datum->strs = p2;
+ datum->backpatch = -1;
+ datum->label_ = -1;
+ return datum;
+}
+
+/*==========================================
+ * スクリプトバッファに1バイト書き込む
+ *------------------------------------------
+ */
+void ScriptBuffer::add_scriptc(ByteCode a)
+{
+ script_buf.push_back(a);
+}
+
+/*==========================================
+ * スクリプトバッファにデータタイプを書き込む
+ *------------------------------------------
+ */
+void ScriptBuffer::add_scriptb(uint8_t a)
+{
+ add_scriptc(static_cast<ByteCode>(a));
+}
+
+/*==========================================
+ * スクリプトバッファに整数を書き込む
+ *------------------------------------------
+ */
+void ScriptBuffer::add_scripti(uint32_t a)
+{
+ while (a >= 0x40)
+ {
+ add_scriptb(a | 0xc0);
+ a = (a - 0x40) >> 6;
+ }
+ add_scriptb(a | 0x80);
+}
+
+/*==========================================
+ * スクリプトバッファにラベル/変数/関数を書き込む
+ *------------------------------------------
+ */
+// 最大16Mまで
+void ScriptBuffer::add_scriptl(P<str_data_t> ld)
+{
+ int backpatch = ld->backpatch;
+
+ switch (ld->type)
+ {
+ case StringCode::POS:
+ add_scriptc(ByteCode::POS);
+ add_scriptb(static_cast<uint8_t>(ld->label_));
+ add_scriptb(static_cast<uint8_t>(ld->label_ >> 8));
+ add_scriptb(static_cast<uint8_t>(ld->label_ >> 16));
+ break;
+ case StringCode::NOP:
+ // need to set backpatch, because it might become a label later
+ add_scriptc(ByteCode::VARIABLE);
+ ld->backpatch = script_buf.size();
+ add_scriptb(static_cast<uint8_t>(backpatch));
+ add_scriptb(static_cast<uint8_t>(backpatch >> 8));
+ add_scriptb(static_cast<uint8_t>(backpatch >> 16));
+ break;
+ case StringCode::INT:
+ add_scripti(ld->val);
+ break;
+ case StringCode::FUNC:
+ add_scriptc(ByteCode::FUNC_REF);
+ add_scriptb(static_cast<uint8_t>(ld->val));
+ add_scriptb(static_cast<uint8_t>(ld->val >> 8));
+ add_scriptb(static_cast<uint8_t>(ld->val >> 16));
+ break;
+ case StringCode::PARAM:
+ add_scriptc(ByteCode::PARAM);
+ add_scriptb(static_cast<uint8_t>(ld->val));
+ add_scriptb(static_cast<uint8_t>(ld->val >> 8));
+ add_scriptb(static_cast<uint8_t>(ld->val >> 16));
+ break;
+ default:
+ abort();
+ }
+}
+
+/*==========================================
+ * ラベルを解決する
+ *------------------------------------------
+ */
+void ScriptBuffer::set_label(Borrowed<str_data_t> ld, int pos_)
+{
+ int next;
+
+ ld->type = StringCode::POS;
+ ld->label_ = pos_;
+ for (int i = ld->backpatch; i >= 0 && i != 0x00ffffff; i = next)
+ {
+ next = 0;
+ // woot! no longer endian-dependent!
+ next |= static_cast<uint8_t>(script_buf[i + 0]) << 0;
+ next |= static_cast<uint8_t>(script_buf[i + 1]) << 8;
+ next |= static_cast<uint8_t>(script_buf[i + 2]) << 16;
+ script_buf[i - 1] = ByteCode::POS;
+ script_buf[i] = static_cast<ByteCode>(pos_);
+ script_buf[i + 1] = static_cast<ByteCode>(pos_ >> 8);
+ script_buf[i + 2] = static_cast<ByteCode>(pos_ >> 16);
+ }
+}
+
+/*==========================================
+ * スペース/コメント読み飛ばし
+ *------------------------------------------
+ */
+static
+ZString::iterator skip_space(ZString::iterator p)
+{
+ while (1)
+ {
+ while (isspace(*p))
+ p++;
+ if (p[0] == '/' && p[1] == '/')
+ {
+ while (*p && *p != '\n')
+ p++;
+ }
+ else if (p[0] == '/' && p[1] == '*')
+ {
+ p++;
+ while (*p && (p[-1] != '*' || p[0] != '/'))
+ p++;
+ if (*p)
+ p++;
+ }
+ else
+ break;
+ }
+ return p;
+}
+
+/*==========================================
+ * 1単語スキップ
+ *------------------------------------------
+ */
+static
+ZString::iterator skip_word(ZString::iterator p)
+{
+ // prefix
+ if (*p == '$')
+ p++; // MAP鯖内共有変数用
+ if (*p == '@')
+ p++; // 一時的変数用(like weiss)
+ if (*p == '#')
+ p++; // account変数用
+ if (*p == '#')
+ p++; // ワールドaccount変数用
+
+ while (isalnum(*p) || *p == '_')
+ p++;
+
+ // postfix
+ if (*p == '$')
+ p++; // 文字列変数
+
+ return p;
+}
+
+/*==========================================
+ * エラーメッセージ出力
+ *------------------------------------------
+ */
+static
+void disp_error_message(ZString mes, ZString::iterator pos_)
+{
+ script_errors++;
+
+ assert (startptr.begin() <= pos_ && pos_ <= startptr.end());
+
+ int line;
+ ZString::iterator p;
+
+ for (line = startline, p = startptr.begin(); p != startptr.end(); line++)
+ {
+ ZString::iterator linestart = p;
+ ZString::iterator lineend = std::find(p, startptr.end(), '\n');
+ if (pos_ < lineend)
+ {
+ PRINTF("\n%s\nline %d : "_fmt, mes, line);
+ for (int i = 0; linestart + i != lineend; i++)
+ {
+ if (linestart + i != pos_)
+ PRINTF("%c"_fmt, linestart[i]);
+ else
+ PRINTF("\'%c\'"_fmt, linestart[i]);
+ }
+ PRINTF("\a\n"_fmt);
+ return;
+ }
+ p = lineend + 1;
+ }
+}
+
+/*==========================================
+ * 項の解析
+ *------------------------------------------
+ */
+ZString::iterator ScriptBuffer::parse_simpleexpr(ZString::iterator p)
+{
+ p = skip_space(p);
+
+ if (*p == ';' || *p == ',')
+ {
+ disp_error_message("unexpected expr end"_s, p);
+ exit(1);
+ }
+ if (*p == '(')
+ {
+
+ p = parse_subexpr(p + 1, -1);
+ p = skip_space(p);
+ if ((*p++) != ')')
+ {
+ disp_error_message("unmatch ')'"_s, p);
+ exit(1);
+ }
+ }
+ else if (isdigit(*p) || ((*p == '-' || *p == '+') && isdigit(p[1])))
+ {
+ char *np;
+ int i = strtoul(&*p, &np, 0);
+ add_scripti(i);
+ p += np - &*p;
+ }
+ else if (*p == '"')
+ {
+ add_scriptc(ByteCode::STR);
+ p++;
+ while (*p && *p != '"')
+ {
+ if (*p == '\\')
+ p++;
+ else if (*p == '\n')
+ {
+ disp_error_message("unexpected newline @ string"_s, p);
+ exit(1);
+ }
+ add_scriptb(*p++);
+ }
+ if (!*p)
+ {
+ disp_error_message("unexpected eof @ string"_s, p);
+ exit(1);
+ }
+ add_scriptb(0);
+ p++; //'"'
+ }
+ else
+ {
+ // label , register , function etc
+ ZString::iterator p2 = skip_word(p);
+ if (p2 == p)
+ {
+ disp_error_message("unexpected character"_s, p);
+ exit(1);
+ }
+ XString word(&*p, &*p2, nullptr);
+ if (word.startswith("On"_s) || word.startswith("L_"_s) || word.startswith("S_"_s))
+ probable_labels.insert(stringish<ScriptLabel>(word));
+ if (parse_cmd_if && (word == "callsub"_s || word == "callfunc"_s || word == "return"_s))
+ {
+ disp_error_message("Sorry, callsub/callfunc/return have never worked properly in an if statement."_s, p);
+ }
+ P<str_data_t> ld = add_strp(word);
+
+ parse_cmdp = Some(ld); // warn_*_mismatch_paramnumのために必要
+ // why not just check l->str == "if"_s or std::string(p, p2) == "if"_s?
+ if (Some(ld) == search_strp("if"_s)) // warn_cmd_no_commaのために必要
+ parse_cmd_if++;
+ p = p2;
+
+ if (ld->type != StringCode::FUNC && *p == '[')
+ {
+ // array(name[i] => getelementofarray(name,i) )
+ add_scriptl(TRY_UNWRAP(search_strp("getelementofarray"_s), abort()));
+ add_scriptc(ByteCode::ARG);
+ add_scriptl(ld);
+ p = parse_subexpr(p + 1, -1);
+ p = skip_space(p);
+ if (*p != ']')
+ {
+ disp_error_message("unmatch ']'"_s, p);
+ exit(1);
+ }
+ p++;
+ add_scriptc(ByteCode::FUNC);
+ }
+ else
+ add_scriptl(ld);
+
+ }
+
+ return p;
+}
+
+/*==========================================
+ * 式の解析
+ *------------------------------------------
+ */
+ZString::iterator ScriptBuffer::parse_subexpr(ZString::iterator p, int limit)
+{
+ ByteCode op;
+ int opl, len;
+
+ p = skip_space(p);
+
+ if (*p == '-')
+ {
+ ZString::iterator tmpp = skip_space(p + 1);
+ if (*tmpp == ';' || *tmpp == ',')
+ {
+ --script_errors; disp_error_message("deprecated: implicit 'next statement' label"_s, p);
+ add_scriptl(borrow(LABEL_NEXTLINE_));
+ p++;
+ return p;
+ }
+ }
+ ZString::iterator tmpp = p;
+ if ((op = ByteCode::NEG, *p == '-') || (op = ByteCode::LNOT, *p == '!')
+ || (op = ByteCode::NOT, *p == '~'))
+ {
+ p = parse_subexpr(p + 1, 100);
+ add_scriptc(op);
+ }
+ else
+ p = parse_simpleexpr(p);
+ p = skip_space(p);
+ while (((op = ByteCode::ADD, opl = 6, len = 1, *p == '+') ||
+ (op = ByteCode::SUB, opl = 6, len = 1, *p == '-') ||
+ (op = ByteCode::MUL, opl = 7, len = 1, *p == '*') ||
+ (op = ByteCode::DIV, opl = 7, len = 1, *p == '/') ||
+ (op = ByteCode::MOD, opl = 7, len = 1, *p == '%') ||
+ (op = ByteCode::FUNC, opl = 8, len = 1, *p == '(') ||
+ (op = ByteCode::LAND, opl = 1, len = 2, *p == '&' && p[1] == '&') ||
+ (op = ByteCode::AND, opl = 5, len = 1, *p == '&') ||
+ (op = ByteCode::LOR, opl = 0, len = 2, *p == '|' && p[1] == '|') ||
+ (op = ByteCode::OR, opl = 4, len = 1, *p == '|') ||
+ (op = ByteCode::XOR, opl = 3, len = 1, *p == '^') ||
+ (op = ByteCode::EQ, opl = 2, len = 2, *p == '=' && p[1] == '=') ||
+ (op = ByteCode::NE, opl = 2, len = 2, *p == '!' && p[1] == '=') ||
+ (op = ByteCode::R_SHIFT, opl = 5, len = 2, *p == '>' && p[1] == '>') ||
+ (op = ByteCode::GE, opl = 2, len = 2, *p == '>' && p[1] == '=') ||
+ (op = ByteCode::GT, opl = 2, len = 1, *p == '>') ||
+ (op = ByteCode::L_SHIFT, opl = 5, len = 2, *p == '<' && p[1] == '<') ||
+ (op = ByteCode::LE, opl = 2, len = 2, *p == '<' && p[1] == '=') ||
+ (op = ByteCode::LT, opl = 2, len = 1, *p == '<')) && opl > limit)
+ {
+ p += len;
+ if (op == ByteCode::FUNC)
+ {
+ int i = 0;
+ P<str_data_t> funcp = TRY_UNWRAP(parse_cmdp, abort());
+ Array<ZString::iterator, 128> plist;
+
+ if (funcp->type != StringCode::FUNC)
+ {
+ disp_error_message("expect function"_s, tmpp);
+ exit(0);
+ }
+
+ add_scriptc(ByteCode::ARG);
+ while (*p && *p != ')' && i < 128)
+ {
+ plist[i] = p;
+ p = parse_subexpr(p, -1);
+ p = skip_space(p);
+ if (*p == ',')
+ p++;
+ else if (*p != ')' && script_config.warn_func_no_comma)
+ {
+ disp_error_message("expect ',' or ')' at func params"_s,
+ p);
+ }
+ p = skip_space(p);
+ i++;
+ }
+ if (i == 128)
+ {
+ disp_error_message("PANIC: unrecoverable error in function argument list"_s, p);
+ abort();
+ }
+ plist[i] = p;
+ if (*p != ')')
+ {
+ disp_error_message("func request '(' ')'"_s, p);
+ exit(1);
+ }
+ p++;
+
+ if (funcp->type == StringCode::FUNC
+ && script_config.warn_func_mismatch_paramnum)
+ {
+ ZString arg = builtin_functions[funcp->val].arg;
+ int j = 0;
+ // TODO handle ? and multiple * correctly
+ for (j = 0; arg[j]; j++)
+ if (arg[j] == '*' || arg[j] == '?')
+ break;
+ if ((arg[j] == 0 && i != j) || ((arg[j] == '*' || arg[j] == '?') && i < j))
+ {
+ disp_error_message("illegal number of parameters"_s,
+ plist[std::min(i, j)]);
+ }
+ if (!builtin_functions[funcp->val].ret)
+ {
+ disp_error_message("statement in function context"_s, tmpp);
+ }
+ }
+ }
+ else // not op == ByteCode::FUNC
+ {
+ p = parse_subexpr(p, opl);
+ }
+ add_scriptc(op);
+ p = skip_space(p);
+ }
+ return p; /* return first untreated operator */
+}
+
+/*==========================================
+ * 式の評価
+ *------------------------------------------
+ */
+ZString::iterator ScriptBuffer::parse_expr(ZString::iterator p)
+{
+ switch (*p)
+ {
+ case ')':
+ case ';':
+ case ':':
+ case '[':
+ case ']':
+ case '}':
+ disp_error_message("unexpected char"_s, p);
+ exit(1);
+ }
+ p = parse_subexpr(p, -1);
+ return p;
+}
+
+/*==========================================
+ * 行の解析
+ *------------------------------------------
+ */
+ZString::iterator ScriptBuffer::parse_line(ZString::iterator p, bool *can_step)
+{
+ int i = 0;
+ Array<ZString::iterator, 128> plist;
+
+ p = skip_space(p);
+ if (*p == ';')
+ {
+ disp_error_message("Double semi-colon"_s, p);
+ ++p;
+ return p;
+ }
+
+ parse_cmd_if = 0; // warn_cmd_no_commaのために必要
+
+ // 最初は関数名
+ ZString::iterator p2 = p;
+ p = parse_simpleexpr(p);
+ p = skip_space(p);
+
+ P<str_data_t> cmd = TRY_UNWRAP(parse_cmdp, abort());
+ if (cmd->type != StringCode::FUNC)
+ {
+ disp_error_message("expect command"_s, p2);
+ }
+
+ {
+ // TODO should be LString, but no heterogenous lookup yet
+ static
+ std::set<ZString> terminators =
+ {
+ "goto"_s,
+ "return"_s,
+ "close"_s,
+ "menu"_s,
+ "end"_s,
+ "mapexit"_s,
+ "shop"_s,
+ };
+ *can_step = terminators.count(cmd->strs) == 0;
+ }
+
+ add_scriptc(ByteCode::ARG);
+ while (*p && *p != ';' && i < 128)
+ {
+ plist[i] = p;
+
+ p = parse_expr(p);
+ p = skip_space(p);
+ // 引数区切りの,処理
+ if (*p == ',')
+ p++;
+ else if (*p != ';' && script_config.warn_cmd_no_comma
+ && parse_cmd_if * 2 <= i)
+ {
+ disp_error_message("expect ',' or ';' at cmd params"_s, p);
+ }
+ p = skip_space(p);
+ i++;
+ }
+ if (i == 128)
+ {
+ disp_error_message("PANIC: unknown error in command argument list"_s, p);
+ abort();
+ }
+ plist[i] = p;
+ if (*(p++) != ';')
+ {
+ disp_error_message("need ';'"_s, p);
+ exit(1);
+ }
+ add_scriptc(ByteCode::FUNC);
+
+ if (cmd->type == StringCode::FUNC
+ && script_config.warn_cmd_mismatch_paramnum)
+ {
+ ZString arg = builtin_functions[cmd->val].arg;
+ int j = 0;
+ // TODO see above
+ for (j = 0; arg[j]; j++)
+ if (arg[j] == '*' || arg[j] == '?')
+ break;
+ if ((arg[j] == 0 && i != j) || ((arg[j] == '*' || arg[j] == '?') && i < j))
+ {
+ disp_error_message("illegal number of parameters"_s,
+ plist[std::min(i, j)]);
+ }
+ if (builtin_functions[cmd->val].ret)
+ {
+ disp_error_message("function in statement context"_s, p2);
+ }
+ }
+
+ return p;
+}
+
+/*==========================================
+ * 組み込み関数の追加
+ *------------------------------------------
+ */
+static
+void add_builtin_functions(void)
+{
+ for (int i = 0; builtin_functions[i].func; i++)
+ {
+ P<str_data_t> n = add_strp(builtin_functions[i].name);
+ n->type = StringCode::FUNC;
+ n->val = i;
+ }
+}
+
+std::unique_ptr<const ScriptBuffer> compile_script(RString debug_name, const ast::script::ScriptBody& body, bool implicit_end)
+{
+ auto script_buf = make_unique<ScriptBuffer>(std::move(debug_name));
+ script_buf->parse_script(body.braced_body, body.span.begin.line, implicit_end);
+ return std::move(script_buf);
+}
+
+/*==========================================
+ * スクリプトの解析
+ *------------------------------------------
+ */
+void ScriptBuffer::parse_script(ZString src, int line, bool implicit_end)
+{
+ static int first = 1;
+
+ if (first)
+ {
+ add_builtin_functions();
+ }
+ first = 0;
+ LABEL_NEXTLINE_.type = StringCode::NOP;
+ LABEL_NEXTLINE_.backpatch = -1;
+ LABEL_NEXTLINE_.label_ = -1;
+ for (auto& pair : str_datam)
+ {
+ str_data_t& dit = pair.second;
+ if (dit.type == StringCode::POS || dit.type == StringCode::VARIABLE)
+ {
+ dit.type = StringCode::NOP;
+ dit.backpatch = -1;
+ dit.label_ = -1;
+ }
+ }
+
+ // 外部用label dbの初期化
+ scriptlabel_db.clear();
+
+ // for error message
+ startptr = src;
+ startline = line;
+
+ bool can_step = true;
+
+ ZString::iterator p = src.begin();
+ p = skip_space(p);
+ if (*p != '{')
+ {
+ disp_error_message("not found '{'"_s, p);
+ abort();
+ }
+ for (p++; *p && *p != '}';)
+ {
+ p = skip_space(p);
+ if (*skip_space(skip_word(p)) == ':')
+ {
+ if (can_step)
+ {
+ --script_errors; disp_error_message("deprecated: implicit fallthrough"_s, p);
+ }
+ can_step = true;
+
+ ZString::iterator tmpp = skip_word(p);
+ XString str(&*p, &*tmpp, nullptr);
+ P<str_data_t> ld = add_strp(str);
+ bool e1 = ld->type != StringCode::NOP;
+ bool e2 = ld->type == StringCode::POS;
+ bool e3 = ld->label_ != -1;
+ assert (e1 == e2 && e2 == e3);
+ if (e3)
+ {
+ disp_error_message("dup label "_s, p);
+ exit(1);
+ }
+ set_label(ld, script_buf.size());
+ scriptlabel_db.insert(stringish<ScriptLabel>(str), script_buf.size());
+ debug_labels.push_back(std::make_pair(stringish<ScriptLabel>(str), script_buf.size()));
+ p = tmpp + 1;
+ continue;
+ }
+
+ if (!can_step)
+ {
+ --script_errors; disp_error_message("deprecated: unreachable statement"_s, p);
+ }
+ // 他は全部一緒くた
+ p = parse_line(p, &can_step);
+ p = skip_space(p);
+ add_scriptc(ByteCode::EOL);
+
+ set_label(borrow(LABEL_NEXTLINE_), script_buf.size());
+ LABEL_NEXTLINE_.type = StringCode::NOP;
+ LABEL_NEXTLINE_.backpatch = -1;
+ LABEL_NEXTLINE_.label_ = -1;
+ }
+
+ if (can_step && !implicit_end)
+ {
+ --script_errors; disp_error_message("deprecated: implicit end"_s, p);
+ }
+ add_scriptc(ByteCode::NOP);
+
+ // resolve the unknown labels
+ for (auto& pair : str_datam)
+ {
+ str_data_t& sit = pair.second;
+ if (sit.type == StringCode::NOP)
+ {
+ sit.type = StringCode::VARIABLE;
+ sit.label_ = 0; // anything but -1. Shouldn't matter, but helps asserts.
+ size_t pool_index = variable_names.intern(sit.strs);
+ for (int next, j = sit.backpatch; j >= 0 && j != 0x00ffffff; j = next)
+ {
+ next = 0;
+ next |= static_cast<uint8_t>(script_buf[j + 0]) << 0;
+ next |= static_cast<uint8_t>(script_buf[j + 1]) << 8;
+ next |= static_cast<uint8_t>(script_buf[j + 2]) << 16;
+ script_buf[j] = static_cast<ByteCode>(pool_index);
+ script_buf[j + 1] = static_cast<ByteCode>(pool_index >> 8);
+ script_buf[j + 2] = static_cast<ByteCode>(pool_index >> 16);
+ }
+ }
+ }
+
+ for (const auto& pair : scriptlabel_db)
+ {
+ ScriptLabel key = pair.first;
+ if (key.startswith("On"_s))
+ continue;
+ if (!(key.startswith("L_"_s) || key.startswith("S_"_s)))
+ PRINTF("Warning: ugly label: %s\n"_fmt, key);
+ else if (!probable_labels.count(key))
+ PRINTF("Warning: unused label: %s\n"_fmt, key);
+ }
+ for (ScriptLabel used : probable_labels)
+ {
+ if (scriptlabel_db.search(used).is_none())
+ PRINTF("Warning: no such label: %s\n"_fmt, used);
+ }
+ probable_labels.clear();
+
+ if (!DEBUG_DISP)
+ return;
+ for (size_t i = 0; i < script_buf.size(); i++)
+ {
+ if ((i & 15) == 0)
+ PRINTF("%04zx : "_fmt, i);
+ PRINTF("%02x "_fmt, script_buf[i]);
+ if ((i & 15) == 15)
+ PRINTF("\n"_fmt);
+ }
+ PRINTF("\n"_fmt);
+}
+} // namespace map
+} // namespace tmwa
diff --git a/src/tests/test.cpp b/src/map/script-parse.hpp
index d731d7c..e08c470 100644
--- a/src/tests/test.cpp
+++ b/src/map/script-parse.hpp
@@ -1,6 +1,9 @@
-// test.cpp - Driver for testwuite
+#pragma once
+// script-parse.hpp - EAthena script frontend, engine, and library.
//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -17,17 +20,17 @@
// 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 <gtest/gtest.h>
+#include "fwd.hpp"
-#include "../poison.hpp"
+#include <memory>
+
+#include "script-buffer.hpp"
namespace tmwa
{
-} // namespace tmwa
-
-int main(int argc, char **argv)
+namespace map
{
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
+std::unique_ptr<const ScriptBuffer> compile_script(RString debug_name, const ast::script::ScriptBody& body, bool implicit_end);
+} // namespace map
+} // namespace tmwa
diff --git a/src/map/script-parse.py b/src/map/script-parse.py
new file mode 100644
index 0000000..0309f54
--- /dev/null
+++ b/src/map/script-parse.py
@@ -0,0 +1,553 @@
+class ScriptBuffer(object):
+ __slots__ = ('_value')
+ name = 'tmwa::map::ScriptBuffer'
+ enabled = True
+
+ def __init__(self, value):
+ self._value = value
+
+ def to_string(self):
+ return self._value['debug_name']
+
+ def get_com(self, b, i, r, labels_dict):
+ # see script-parse-internal.hpp:ByteCode and script-call.cpp:get_com
+ ops = '''
+ NOP, POS, INT, PARAM, FUNC, STR, ARG,
+ VARIABLE, EOL,
+ LOR, LAND, LE, LT, GE, GT, EQ, NE,
+ XOR, OR, AND, ADD, SUB, MUL, DIV, MOD,
+ NEG, LNOT, NOT, R_SHIFT, L_SHIFT,
+ FUNC_REF,
+ '''.replace(',', '').split()
+ ci = int(b[i])
+ if ci >= 0x80:
+ rv = 0
+ sh = 0
+ # Because of how the python iterator up a frame consumed the i already,
+ # have to manually unroll the first iteration of the C loop.
+ # TODO itertools.chain([i], r) or something?
+ if True:
+ if True:
+ rv += (ci & 0x7f) << sh
+ sh += 6
+ if ci >= 0xc0:
+ while True:
+ i = next(r)
+ ci = int(b[i])
+
+ rv += (ci & 0x7f) << sh
+ sh += 6
+ if not (ci >= 0xc0):
+ break
+ return 'INT %d' % rv
+
+ cs = ops[ci]
+ if cs in {'POS', 'VARIABLE', 'FUNC_REF', 'PARAM'}:
+ ai = 0
+ ai |= (int(b[next(r)]) << 0)
+ ai |= (int(b[next(r)]) << 8)
+ ai |= (int(b[next(r)]) << 16)
+ if cs == 'POS':
+ ln = labels_dict.get(ai)
+ if ln is not None:
+ return 'POS %d %s' % (ai, ln[0])
+ elif cs == 'VARIABLE':
+ global rstring_disable_children
+ rstring_disable_children = True
+ try:
+ rv = 'VARIABLE %s' % gdb.parse_and_eval('tmwa::map::variable_names.names._M_impl._M_start[{ai}]'.format(ai=ai))
+ finally:
+ rstring_disable_children = False
+ return rv
+ elif cs == 'FUNC_REF':
+ return 'FUNC_REF %s' % gdb.parse_and_eval('tmwa::map::builtin_functions[{ai}].name'.format(ai=ai))
+ elif cs == 'PARAM':
+ # https://sourceware.org/bugzilla/show_bug.cgi?id=17568
+ try:
+ # this should work
+ SP = gdb.lookup_type('tmwa::SP')
+ except gdb.error:
+ # this should not work
+ SP = gdb.parse_and_eval('tmwa::SP').type
+ return 'PARAM %s' % gdb.Value(ai).cast(SP)
+ else:
+ assert False
+ return '%s %d' % (cs, ai)
+ elif cs == 'STR':
+ buf = bytearray()
+ while True:
+ i = next(r)
+ ci = int(b[i])
+ if ci == 0:
+ break
+ buf.append(ci)
+ return 'STR "%s"' % str(buf).replace('\\', '\\\\').replace('"', '\\"')
+ elif cs == 'EOL':
+ return cs + '\n'
+ return cs
+
+ def children(self):
+ labels = self._value['debug_labels']
+ labels_begin = labels['_M_impl']['_M_start']
+ labels_end = labels['_M_impl']['_M_finish']
+ labels_size = int(labels_end - labels_begin)
+ labels_list = [labels_begin[i] for i in range(labels_size)]
+ labels_dict = {}
+ char_ptr = gdb.lookup_type('char').pointer()
+ for e in labels_list:
+ offset = int(e['second'])
+ label_name = str(e['first'].address.cast(char_ptr))
+ labels_dict.setdefault(offset, []).append(label_name)
+ code = self._value['script_buf']
+ code_begin = code['_M_impl']['_M_start']
+ code_end = code['_M_impl']['_M_finish']
+ code_size = int(code_end - code_begin)
+ r = iter(range(code_size))
+ for i in r:
+ buf = []
+ for label in labels_dict.get(i, []):
+ if label.startswith('On'):
+ yield 'blah', 'event %s:' % label
+ else:
+ yield 'blah', 'label %s:' % label
+ c = self.get_com(code_begin, i, r, labels_dict)
+ yield 'blah', '%6d: %s' % (i, c)
+
+
+ def display_hint(self):
+ return 'array'
+
+ src = '''
+ {
+ end;
+ S_Sub:
+ return;
+ OnFoo:
+ callsub S_Sub;
+ setarray @a,
+ -1, 0, 1,
+ 0x0, 0x1, 0x2,
+ 0x1, 0x2, 0x3,
+ 0x3, 0x4, 0x5,
+ 0x7, 0x8, 0x9,
+ 0xf, 0x10, 0x11,
+ 0x1f, 0x20, 0x21,
+ 0x3f, 0x40, 0x41,
+ 0x7f, 0x80, 0x81,
+ 0xff, 0x100, 0x101,
+ 0x1ff, 0x200, 0x201,
+ 0x3ff, 0x400, 0x401,
+ 0x7ff, 0x800, 0x801,
+ 0xfff, 0x1000, 0x1001,
+ 0x1fff, 0x2000, 0x2001,
+ 0x3fff, 0x4000, 0x4001,
+ 0x7fff, 0x8000, 0x8001,
+ 0xffff, 0x10000, 0x10001,
+ 0x1ffff, 0x20000, 0x20001,
+ 0x3ffff, 0x40000, 0x40001,
+ 0x7ffff, 0x80000, 0x80001,
+ 0xfffff, 0x100000, 0x100001,
+ 0x1fffff, 0x200000, 0x200001,
+ 0x3fffff, 0x400000, 0x400001,
+ 0x7fffff, 0x800000, 0x800001,
+ 0xffffff, 0x1000000, 0x1000001,
+ 0x1ffffff, 0x2000000, 0x2000001,
+ 0x3ffffff, 0x4000000, 0x4000001,
+ 0x7ffffff, 0x8000000, 0x8000001,
+ 0xfffffff, 0x10000000, 0x10000001,
+ 0x1fffffff, 0x20000000, 0x20000001,
+ 0x3fffffff, 0x40000000, 0x40000001,
+ 0x7fffffff, 0x80000000, 0x80000001,
+ 0xffffffff;
+ set TEST_FAKE_PARAM_BASELEVEL, TEST_FAKE_CONSTANT;
+ set @s$, "hello";
+ set @i, a || b;
+ set @i, a && b;
+ set @i, a <= b;
+ set @i, a < b;
+ set @i, a >= b;
+ set @i, a > b;
+ set @i, a == b;
+ set @i, a != b;
+ set @i, a ^ b;
+ set @i, a | b;
+ set @i, a & b;
+ set @i, a + b;
+ set @i, a - b;
+ set @i, a * b;
+ set @i, a / b;
+ set @i, a % b;
+ set @i, - b;
+ set @i, ! b;
+ set @i, ~ b;
+ set @i, a >> b;
+ set @i, a << b;
+ }
+ '''.replace('\n', ' ')
+
+ asm = ''\
+ +''' 0: FUNC_REF "end",
+ 4: ARG,
+ 5: FUNC,
+ 6: EOL
+ ,
+ label "S_Sub":,
+ 7: FUNC_REF "return",
+ 11: ARG,
+ 12: FUNC,
+ 13: EOL
+ ,
+ label "OnFoo":,
+ 14: FUNC_REF "callsub",
+ 18: ARG,
+ 19: POS 7 "S_Sub",
+ 23: FUNC,
+ 24: EOL
+ ,
+ 25: FUNC_REF "setarray",
+ 29: ARG,
+ 30: VARIABLE "@a",
+ 34: INT 1,
+ 35: NEG,
+ 36: INT 0,
+ 37: INT 1,
+ 38: INT 0,
+ 39: INT 1,
+ 40: INT 2,
+ 41: INT 1,
+ 42: INT 2,
+ 43: INT 3,
+ 44: INT 3,
+ 45: INT 4,
+ 46: INT 5,
+ 47: INT 7,
+ 48: INT 8,
+ 49: INT 9,
+ 50: INT 15,
+ 51: INT 16,
+ 52: INT 17,
+ 53: INT 31,
+ 54: INT 32,
+ 55: INT 33,
+ 56: INT 63,
+ 57: INT 64,
+ 59: INT 65,
+ 61: INT 127,
+ 63: INT 128,
+ 65: INT 129,
+ 67: INT 255,
+ 69: INT 256,
+ 71: INT 257,
+ 73: INT 511,
+ 75: INT 512,
+ 77: INT 513,
+ 79: INT 1023,
+ 81: INT 1024,
+ 83: INT 1025,
+ 85: INT 2047,
+ 87: INT 2048,
+ 89: INT 2049,
+ 91: INT 4095,
+ 93: INT 4096,
+ 95: INT 4097,
+ 97: INT 8191,
+ 100: INT 8192,
+ 103: INT 8193,
+ 106: INT 16383,
+ 109: INT 16384,
+ 112: INT 16385,
+ 115: INT 32767,
+ 118: INT 32768,
+ 121: INT 32769,
+ 124: INT 65535,
+ 127: INT 65536,
+ 130: INT 65537,
+ 133: INT 131071,
+ 136: INT 131072,
+ 139: INT 131073,
+ 142: INT 262143,
+ 145: INT 262144,
+ 148: INT 262145,
+ 151: INT 524287,
+ 155: INT 524288,
+ 159: INT 524289,
+ 163: INT 1048575,
+ 167: INT 1048576,
+ 171: INT 1048577,
+ 175: INT 2097151,
+ 179: INT 2097152,
+ 183: INT 2097153,
+ 187: INT 4194303,
+ 191: INT 4194304,
+ 195: INT 4194305,
+ 199: INT 8388607,
+ 203: INT 8388608,
+ 207: INT 8388609,
+ 211: INT 16777215,
+ 215: INT 16777216,
+ 219: INT 16777217,
+ 223: INT 33554431,
+ 228: INT 33554432,
+ 233: INT 33554433,
+ 238: INT 67108863,
+ 243: INT 67108864,
+ 248: INT 67108865,
+ 253: INT 134217727,
+ 258: INT 134217728,
+ 263: INT 134217729,
+ 268: INT 268435455,
+ 273: INT 268435456,
+ 278: INT 268435457,
+ 283: INT 536870911,
+ 288: INT 536870912,
+ 293: INT 536870913,
+ 298: INT 1073741823,
+ 303: INT 1073741824,
+ 308: INT 1073741825,
+ 313: INT 2147483647,
+ 319: INT 2147483648,
+ 325: INT 2147483649,
+ 331: INT 4294967295,
+ 337: FUNC,
+ 338: EOL
+ ,
+ 339: FUNC_REF "set",
+ 343: ARG,
+ 344: PARAM tmwa::SP::BASELEVEL,
+ 348: INT 42,
+ 349: FUNC,
+ 350: EOL
+ ,
+ 351: FUNC_REF "set",
+ 355: ARG,
+ 356: VARIABLE "@s$",
+ 360: STR "hello",
+ 367: FUNC,
+ 368: EOL
+ ,
+ 369: FUNC_REF "set",
+ 373: ARG,
+ 374: VARIABLE "@i",
+ 378: VARIABLE "a",
+ 382: VARIABLE "b",
+ 386: LOR,
+ 387: FUNC,
+ 388: EOL
+ ,
+ 389: FUNC_REF "set",
+ 393: ARG,
+ 394: VARIABLE "@i",
+ 398: VARIABLE "a",
+ 402: VARIABLE "b",
+ 406: LAND,
+ 407: FUNC,
+ 408: EOL
+ ,
+ 409: FUNC_REF "set",
+ 413: ARG,
+ 414: VARIABLE "@i",
+ 418: VARIABLE "a",
+ 422: VARIABLE "b",
+ 426: LE,
+ 427: FUNC,
+ 428: EOL
+ ,
+ 429: FUNC_REF "set",
+ 433: ARG,
+ 434: VARIABLE "@i",
+ 438: VARIABLE "a",
+ 442: VARIABLE "b",
+ 446: LT,
+ 447: FUNC,
+ 448: EOL
+ ,
+ 449: FUNC_REF "set",
+ 453: ARG,
+ 454: VARIABLE "@i",
+ 458: VARIABLE "a",
+ 462: VARIABLE "b",
+ 466: GE,
+ 467: FUNC,
+ 468: EOL
+ ,
+ 469: FUNC_REF "set",
+ 473: ARG,
+ 474: VARIABLE "@i",
+ 478: VARIABLE "a",
+ 482: VARIABLE "b",
+ 486: GT,
+ 487: FUNC,
+ 488: EOL
+ ,
+ 489: FUNC_REF "set",
+ 493: ARG,
+ 494: VARIABLE "@i",
+ 498: VARIABLE "a",
+ 502: VARIABLE "b",
+ 506: EQ,
+ 507: FUNC,
+ 508: EOL
+ ,
+ 509: FUNC_REF "set",
+ 513: ARG,
+ 514: VARIABLE "@i",
+ 518: VARIABLE "a",
+ 522: VARIABLE "b",
+ 526: NE,
+ 527: FUNC,
+ 528: EOL
+ ,
+ 529: FUNC_REF "set",
+ 533: ARG,
+ 534: VARIABLE "@i",
+ 538: VARIABLE "a",
+ 542: VARIABLE "b",
+ 546: XOR,
+ 547: FUNC,
+ 548: EOL
+ ,
+ 549: FUNC_REF "set",
+ 553: ARG,
+ 554: VARIABLE "@i",
+ 558: VARIABLE "a",
+ 562: VARIABLE "b",
+ 566: OR,
+ 567: FUNC,
+ 568: EOL
+ ,
+ 569: FUNC_REF "set",
+ 573: ARG,
+ 574: VARIABLE "@i",
+ 578: VARIABLE "a",
+ 582: VARIABLE "b",
+ 586: AND,
+ 587: FUNC,
+ 588: EOL
+ ,
+ 589: FUNC_REF "set",
+ 593: ARG,
+ 594: VARIABLE "@i",
+ 598: VARIABLE "a",
+ 602: VARIABLE "b",
+ 606: ADD,
+ 607: FUNC,
+ 608: EOL
+ ,
+ 609: FUNC_REF "set",
+ 613: ARG,
+ 614: VARIABLE "@i",
+ 618: VARIABLE "a",
+ 622: VARIABLE "b",
+ 626: SUB,
+ 627: FUNC,
+ 628: EOL
+ ,
+ 629: FUNC_REF "set",
+ 633: ARG,
+ 634: VARIABLE "@i",
+ 638: VARIABLE "a",
+ 642: VARIABLE "b",
+ 646: MUL,
+ 647: FUNC,
+ 648: EOL
+ ,
+ 649: FUNC_REF "set",
+ 653: ARG,
+ 654: VARIABLE "@i",
+ 658: VARIABLE "a",
+ 662: VARIABLE "b",
+ 666: DIV,
+ 667: FUNC,
+ 668: EOL
+ ,
+ 669: FUNC_REF "set",
+ 673: ARG,
+ 674: VARIABLE "@i",
+ 678: VARIABLE "a",
+ 682: VARIABLE "b",
+ 686: MOD,
+ 687: FUNC,
+ 688: EOL
+ ,
+ 689: FUNC_REF "set",
+ 693: ARG,
+ 694: VARIABLE "@i",
+ 698: VARIABLE "b",
+ 702: NEG,
+ 703: FUNC,
+ 704: EOL
+ ,
+ 705: FUNC_REF "set",
+ 709: ARG,
+ 710: VARIABLE "@i",
+ 714: VARIABLE "b",
+ 718: LNOT,
+ 719: FUNC,
+ 720: EOL
+ ,
+ 721: FUNC_REF "set",
+ 725: ARG,
+ 726: VARIABLE "@i",
+ 730: VARIABLE "b",
+ 734: NOT,
+ 735: FUNC,
+ 736: EOL
+ ,
+ 737: FUNC_REF "set",
+ 741: ARG,
+ 742: VARIABLE "@i",
+ 746: VARIABLE "a",
+ 750: VARIABLE "b",
+ 754: R_SHIFT,
+ 755: FUNC,
+ 756: EOL
+ ,
+ 757: FUNC_REF "set",
+ 761: ARG,
+ 762: VARIABLE "@i",
+ 766: VARIABLE "a",
+ 770: VARIABLE "b",
+ 774: L_SHIFT,
+ 775: FUNC,
+ 776: EOL
+ ,
+ 777: NOP'''.replace('''
+ ''', '\n')
+
+ test_extra = ('''
+ #include "../compat/borrow.hpp"
+ #include "../io/line.hpp"
+ #include "../mmo/clif.t.hpp"
+ #include "../ast/script.hpp"
+ #include "../map/script-parse-internal.hpp"
+
+ using tmwa::operator "" _s;
+
+ static
+ const tmwa::map::ScriptBuffer& test_script_buffer(tmwa::LString source)
+ {
+ auto p = tmwa::map::add_strp("TEST_FAKE_PARAM_BASELEVEL"_s);
+ p->type = tmwa::map::StringCode::PARAM;
+ p->val = static_cast<uint16_t>(tmwa::SP::BASELEVEL);
+ p = tmwa::map::add_strp("TEST_FAKE_CONSTANT"_s);
+ p->type = tmwa::map::StringCode::INT;
+ p->val = 42;
+
+ tmwa::io::LineCharReader lr(tmwa::io::from_string, "<script debug print test>"_s, source);
+ tmwa::ast::script::ScriptOptions opt;
+ opt.implicit_start = true;
+ opt.implicit_end = true;
+ auto code_res = tmwa::ast::script::parse_script_body(lr, opt);
+ auto code = TRY_UNWRAP(code_res.get_success(), abort());
+ auto ups = tmwa::map::compile_script("script debug print test"_s, code, opt.implicit_end);
+ assert(ups);
+ return *ups.release();
+ }
+ ''')
+
+ # the pretty-printer is designed to be run with 'set print array on'
+ # but the testsuite runs with all settings default, in this case off.
+ tests = [
+ ('test_script_buffer("' + src.replace('\\', '\\\\').replace('"', '\\"') + '"_s)',
+ '"script debug print test" = {' + asm.replace('\n', ' ').replace('EOL ,', 'EOL\n,') + '}'),
+ ]
diff --git a/src/map/script-persist.hpp b/src/map/script-persist.hpp
new file mode 100644
index 0000000..45c2761
--- /dev/null
+++ b/src/map/script-persist.hpp
@@ -0,0 +1,128 @@
+#pragma once
+// script-persist.hpp - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+#include "../compat/borrow.hpp"
+
+#include "../strings/rstring.hpp"
+
+#include "../sexpr/variant.hpp"
+
+#include "../mmo/clif.t.hpp"
+
+#include "script-buffer.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+class SIR
+{
+ uint32_t impl;
+ SIR(SP v)
+ : impl(static_cast<uint32_t>(v))
+ {}
+ SIR(unsigned v, uint8_t i)
+ : impl((i << 24) | v)
+ {}
+public:
+ SIR() : impl() {}
+
+ unsigned base() const { return impl & 0x00ffffff; }
+ uint8_t index() const { return impl >> 24; }
+ SIR iplus(uint8_t i) const { return SIR(base(), index() + i); }
+ static SIR from(unsigned v, uint8_t i=0) { return SIR(v, i); }
+
+ SP sp() const { return static_cast<SP>(impl); }
+ static SIR from(SP v) { return SIR(v); }
+
+ friend bool operator == (SIR l, SIR r) { return l.impl == r.impl; }
+ friend bool operator < (SIR l, SIR r) { return l.impl < r.impl; }
+};
+
+struct ScriptDataPos
+{
+ int numi;
+};
+struct ScriptDataInt
+{
+ int numi;
+};
+struct ScriptDataParam
+{
+ SIR reg;
+};
+struct ScriptDataStr
+{
+ RString str;
+};
+struct ScriptDataArg
+{
+ int numi;
+};
+struct ScriptDataVariable
+{
+ SIR reg;
+};
+struct ScriptDataRetInfo
+{
+ // Not a ScriptPointer - pos is stored in a separate slot,
+ // to avoid exploding the struct for everyone.
+ Borrowed<const ScriptBuffer> script;
+};
+struct ScriptDataFuncRef
+{
+ int numi;
+};
+
+using ScriptDataVariantBase = Variant<
+ ScriptDataPos,
+ ScriptDataInt,
+ ScriptDataParam,
+ ScriptDataStr,
+ ScriptDataArg,
+ ScriptDataVariable,
+ ScriptDataRetInfo,
+ ScriptDataFuncRef
+>;
+struct script_data : ScriptDataVariantBase
+{
+ script_data() = delete;
+ // TODO see if I can delete the copy ctor/assign instead of defaulting
+ script_data(script_data&&) = default;
+ script_data(const script_data&) = default /*delete*/;
+ script_data& operator = (script_data&&) = default;
+ script_data& operator = (const script_data&) = default /*delete*/;
+
+ script_data(ScriptDataPos v) : ScriptDataVariantBase(std::move(v)) {}
+ script_data(ScriptDataInt v) : ScriptDataVariantBase(std::move(v)) {}
+ script_data(ScriptDataParam v) : ScriptDataVariantBase(std::move(v)) {}
+ script_data(ScriptDataStr v) : ScriptDataVariantBase(std::move(v)) {}
+ script_data(ScriptDataArg v) : ScriptDataVariantBase(std::move(v)) {}
+ script_data(ScriptDataVariable v) : ScriptDataVariantBase(std::move(v)) {}
+ script_data(ScriptDataRetInfo v) : ScriptDataVariantBase(std::move(v)) {}
+ script_data(ScriptDataFuncRef v) : ScriptDataVariantBase(std::move(v)) {}
+};
+} // namespace map
+} // namespace tmwa
diff --git a/src/map/script-persist.py b/src/map/script-persist.py
new file mode 100644
index 0000000..3181344
--- /dev/null
+++ b/src/map/script-persist.py
@@ -0,0 +1,37 @@
+class script_data(object):
+ enabled = True
+
+ test_extra = '''
+ #include "../strings/literal.hpp"
+ using tmwa::operator "" _s;
+
+ #include "../map/script-parse-internal.hpp"
+
+ static
+ tmwa::Borrowed<const tmwa::map::ScriptBuffer> fake_script()
+ {
+ static
+ const std::vector<tmwa::map::ByteCode> buffer;
+ return tmwa::borrow(reinterpret_cast<const tmwa::map::ScriptBuffer&>(buffer));
+ }
+
+ '''
+
+ tests = [
+ ('tmwa::map::script_data(tmwa::map::ScriptDataPos{42})',
+ '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataPos) = {numi = 42}}, <No data fields>}'),
+ ('tmwa::map::script_data(tmwa::map::ScriptDataInt{123})',
+ '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataInt) = {numi = 123}}, <No data fields>}'),
+ ('tmwa::map::script_data(tmwa::map::ScriptDataParam{tmwa::map::SIR()})',
+ '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataParam) = {reg = {impl = 0}}}, <No data fields>}'),
+ ('tmwa::map::script_data(tmwa::map::ScriptDataStr{"Hello"_s})',
+ '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataStr) = {str = "Hello"}}, <No data fields>}'),
+ ('tmwa::map::script_data(tmwa::map::ScriptDataArg{0})',
+ '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataArg) = {numi = 0}}, <No data fields>}'),
+ ('tmwa::map::script_data(tmwa::map::ScriptDataVariable{tmwa::map::SIR()})',
+ '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataVariable) = {reg = {impl = 0}}}, <No data fields>}'),
+ ('tmwa::map::script_data(tmwa::map::ScriptDataRetInfo{fake_script()})',
+ '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataRetInfo) = {script = <fake_script()::buffer>}}, <No data fields>}'),
+ ('tmwa::map::script_data(tmwa::map::ScriptDataFuncRef{404})',
+ '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataFuncRef) = {numi = 404}}, <No data fields>}'),
+ ]
diff --git a/src/map/script-startup-internal.hpp b/src/map/script-startup-internal.hpp
new file mode 100644
index 0000000..91ce09b
--- /dev/null
+++ b/src/map/script-startup-internal.hpp
@@ -0,0 +1,36 @@
+#pragma once
+// script-startup-internal.hpp - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "script-startup.hpp"
+#include "fwd.hpp"
+
+#include "script-persist.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+void mapreg_setreg(SIR reg, int val);
+void mapreg_setregstr(SIR reg, XString str);
+} // namespace map
+} // namespace tmwa
diff --git a/src/map/script-startup.cpp b/src/map/script-startup.cpp
new file mode 100644
index 0000000..ad809db
--- /dev/null
+++ b/src/map/script-startup.cpp
@@ -0,0 +1,265 @@
+#include "script-startup-internal.hpp"
+// script-startup.cpp - EAthena script frontend, engine, and library.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011 Chuck Miller
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2013 wushin
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "../strings/zstring.hpp"
+
+#include "../generic/db.hpp"
+#include "../generic/intern-pool.hpp"
+
+#include "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
+#include "../io/read.hpp"
+#include "../io/lock.hpp"
+
+#include "../net/timer.hpp"
+
+#include "globals.hpp"
+#include "map.hpp"
+#include "map_conf.hpp"
+#include "script-parse-internal.hpp"
+#include "script-persist.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace map
+{
+constexpr std::chrono::milliseconds MAPREG_AUTOSAVE_INTERVAL = 10_s;
+
+bool read_constdb(ZString filename)
+{
+ io::ReadFile in(filename);
+ if (!in.is_open())
+ {
+ PRINTF("can't read %s\n"_fmt, filename);
+ return false;
+ }
+
+ bool rv = true;
+ AString line_;
+ while (in.getline(line_))
+ {
+ // is_comment only works for whole-line comments
+ // that could change once the Z dependency is dropped ...
+ LString comment = "//"_s;
+ XString line = line_.xislice_h(std::search(line_.begin(), line_.end(), comment.begin(), comment.end())).rstrip();
+ if (!line)
+ continue;
+ // "%m[A-Za-z0-9_] %i %i"
+
+ // TODO promote either qsplit() or asplit()
+ auto _it = std::find(line.begin(), line.end(), ' ');
+ auto name = line.xislice_h(_it);
+ auto _rest = line.xislice_t(_it);
+ while (_rest.startswith(' '))
+ _rest = _rest.xslice_t(1);
+ auto _it2 = std::find(_rest.begin(), _rest.end(), ' ');
+ auto val_ = _rest.xislice_h(_it2);
+ auto type_ = _rest.xislice_t(_it2);
+ while (type_.startswith(' '))
+ type_ = type_.xslice_t(1);
+ // yes, the above actually DTRT even for underlength input
+
+ int val;
+ int type = 0;
+ // Note for future archeaologists: this code is indented correctly
+ if (std::find_if_not(name.begin(), name.end(),
+ [](char c)
+ {
+ return ('0' <= c && c <= '9')
+ || ('A' <= c && c <= 'Z')
+ || ('a' <= c && c <= 'z')
+ || (c == '_');
+ }) != name.end()
+ || !extract(val_, &val)
+ || (!extract(type_, &type) && type_))
+ {
+ PRINTF("Bad const line: %s\n"_fmt, line_);
+ rv = false;
+ continue;
+ }
+ P<str_data_t> n = add_strp(name);
+ n->type = type ? StringCode::PARAM : StringCode::INT;
+ n->val = val;
+ }
+ return rv;
+}
+
+/*==========================================
+ * マップ変数の変更
+ *------------------------------------------
+ */
+void mapreg_setreg(SIR reg, int val)
+{
+ mapreg_db.put(reg, val);
+
+ mapreg_dirty = 1;
+}
+
+/*==========================================
+ * 文字列型マップ変数の変更
+ *------------------------------------------
+ */
+void mapreg_setregstr(SIR reg, XString str)
+{
+ if (!str)
+ mapregstr_db.erase(reg);
+ else
+ mapregstr_db.insert(reg, str);
+
+ mapreg_dirty = 1;
+}
+
+/*==========================================
+ * 永続的マップ変数の読み込み
+ *------------------------------------------
+ */
+static
+void script_load_mapreg(void)
+{
+ io::ReadFile in(map_conf.mapreg_txt);
+
+ if (!in.is_open())
+ return;
+
+ AString line;
+ while (in.getline(line))
+ {
+ XString buf1, buf2;
+ int index = 0;
+ if (extract(line,
+ record<'\t'>(
+ record<','>(&buf1, &index),
+ &buf2))
+ || extract(line,
+ record<'\t'>(
+ record<','>(&buf1),
+ &buf2)))
+ {
+ int s = variable_names.intern(buf1);
+ SIR key = SIR::from(s, index);
+ if (buf1.back() == '$')
+ {
+ mapregstr_db.insert(key, buf2);
+ }
+ else
+ {
+ int v;
+ if (!extract(buf2, &v))
+ goto borken;
+ mapreg_db.put(key, v);
+ }
+ }
+ else
+ {
+ borken:
+ PRINTF("%s: %s broken data !\n"_fmt, map_conf.mapreg_txt, AString(buf1));
+ continue;
+ }
+ }
+ mapreg_dirty = 0;
+}
+
+/*==========================================
+ * 永続的マップ変数の書き込み
+ *------------------------------------------
+ */
+static
+void script_save_mapreg_intsub(SIR key, int data, io::WriteFile& fp)
+{
+ int num = key.base(), i = key.index();
+ ZString name = variable_names.outtern(num);
+ if (name[1] != '@')
+ {
+ if (i == 0)
+ FPRINTF(fp, "%s\t%d\n"_fmt, name, data);
+ else
+ FPRINTF(fp, "%s,%d\t%d\n"_fmt, name, i, data);
+ }
+}
+
+static
+void script_save_mapreg_strsub(SIR key, ZString data, io::WriteFile& fp)
+{
+ int num = key.base(), i = key.index();
+ ZString name = variable_names.outtern(num);
+ if (name[1] != '@')
+ {
+ if (i == 0)
+ FPRINTF(fp, "%s\t%s\n"_fmt, name, data);
+ else
+ FPRINTF(fp, "%s,%d\t%s\n"_fmt, name, i, data);
+ }
+}
+
+static
+void script_save_mapreg(void)
+{
+ io::WriteLock fp(map_conf.mapreg_txt);
+ if (!fp.is_open())
+ return;
+ for (auto& pair : mapreg_db)
+ script_save_mapreg_intsub(pair.first, pair.second, fp);
+ for (auto& pair : mapregstr_db)
+ script_save_mapreg_strsub(pair.first, pair.second, fp);
+ mapreg_dirty = 0;
+}
+
+static
+void script_autosave_mapreg(TimerData *, tick_t)
+{
+ if (mapreg_dirty)
+ script_save_mapreg();
+}
+
+void do_final_script(void)
+{
+ if (mapreg_dirty >= 0)
+ script_save_mapreg();
+
+ mapreg_db.clear();
+ mapregstr_db.clear();
+ scriptlabel_db.clear();
+ userfunc_db.clear();
+
+ str_datam.clear();
+}
+
+/*==========================================
+ * 初期化
+ *------------------------------------------
+ */
+void do_init_script(void)
+{
+ script_load_mapreg();
+
+ Timer(gettick() + MAPREG_AUTOSAVE_INTERVAL,
+ script_autosave_mapreg,
+ MAPREG_AUTOSAVE_INTERVAL
+ ).detach();
+}
+} // namespace map
+} // namespace tmwa
diff --git a/src/compat/memory.cpp b/src/map/script-startup.hpp
index f9f2c22..1691c94 100644
--- a/src/compat/memory.cpp
+++ b/src/map/script-startup.hpp
@@ -1,7 +1,9 @@
-#include "memory.hpp"
-// memory.cpp - I forget ...
+#pragma once
+// script-startup.hpp - EAthena script frontend, engine, and library.
//
-// Copyright © 2013-2014 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -18,9 +20,15 @@
// 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 "../poison.hpp"
-
+#include "fwd.hpp"
namespace tmwa
{
+namespace map
+{
+void do_init_script(void);
+void do_final_script(void);
+
+bool read_constdb(ZString filename);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/script.cpp b/src/map/script.cpp
deleted file mode 100644
index 40dfc0e..0000000
--- a/src/map/script.cpp
+++ /dev/null
@@ -1,5100 +0,0 @@
-#include "script.hpp"
-// script.cpp - EAthena script frontend, engine, and library.
-//
-// Copyright © ????-2004 Athena Dev Teams
-// Copyright © 2004-2011 The Mana World Development Team
-// Copyright © 2011 Chuck Miller
-// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
-// Copyright © 2013 wushin
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 <cassert>
-#include <cmath>
-#include <cstdlib>
-#include <ctime>
-
-#include <algorithm>
-#include <set>
-
-#include "../compat/fun.hpp"
-
-#include "../strings/mstring.hpp"
-#include "../strings/rstring.hpp"
-#include "../strings/astring.hpp"
-#include "../strings/zstring.hpp"
-#include "../strings/xstring.hpp"
-#include "../strings/literal.hpp"
-
-#include "../generic/db.hpp"
-#include "../generic/intern-pool.hpp"
-#include "../generic/random.hpp"
-
-#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
-#include "../io/lock.hpp"
-#include "../io/read.hpp"
-#include "../io/write.hpp"
-
-#include "../net/socket.hpp"
-#include "../net/timer.hpp"
-
-#include "../mmo/core.hpp"
-#include "../mmo/extract.hpp"
-#include "../mmo/human_time_diff.hpp"
-#include "../mmo/utils.hpp"
-
-#include "atcommand.hpp"
-#include "battle.hpp"
-#include "chrif.hpp"
-#include "clif.hpp"
-#include "intif.hpp"
-#include "itemdb.hpp"
-#include "magic-interpreter-base.hpp"
-#include "map.hpp"
-#include "mob.hpp"
-#include "npc.hpp"
-#include "party.hpp"
-#include "pc.hpp"
-#include "skill.hpp"
-#include "storage.hpp"
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-constexpr bool DEBUG_DISP = false;
-constexpr bool DEBUG_RUN = false;
-
-struct str_data_t
-{
- StringCode type;
- RString strs;
- int backpatch;
- int label_;
- int val;
-};
-static
-Map<RString, str_data_t> str_datam;
-static
-str_data_t LABEL_NEXTLINE_;
-
-static
-DMap<SIR, int> mapreg_db;
-static
-Map<SIR, RString> mapregstr_db;
-static
-int mapreg_dirty = -1;
-AString mapreg_txt = "save/mapreg.txt"_s;
-constexpr std::chrono::milliseconds MAPREG_AUTOSAVE_INTERVAL = 10_s;
-
-Map<ScriptLabel, int> scriptlabel_db;
-static
-std::set<ScriptLabel> probable_labels;
-UPMap<RString, const ScriptBuffer> userfunc_db;
-
-static
-Array<LString, 11> pos_str //=
-{{
- "Head"_s,
- "Body"_s,
- "Left hand"_s,
- "Right hand"_s,
- "Robe"_s,
- "Shoes"_s,
- "Accessory 1"_s,
- "Accessory 2"_s,
- "Head 2"_s,
- "Head 3"_s,
- "Not Equipped"_s,
-}};
-
-static
-struct Script_Config
-{
- static const
- int warn_func_no_comma = 1;
- static const
- int warn_cmd_no_comma = 1;
- static const
- int warn_func_mismatch_paramnum = 1;
- static const
- int warn_cmd_mismatch_paramnum = 1;
- static const
- int check_cmdcount = 8192;
- static const
- int check_gotocount = 512;
-} script_config;
-
-static
-int parse_cmd_if = 0;
-static
-str_data_t *parse_cmdp;
-
-static
-void run_func(ScriptState *st);
-
-static
-void mapreg_setreg(SIR num, int val);
-static
-void mapreg_setregstr(SIR num, XString str);
-
-struct BuiltinFunction
-{
- void (*func)(ScriptState *);
- LString name;
- LString arg;
- char ret;
-};
-// defined later
-extern BuiltinFunction builtin_functions[];
-
-static
-InternPool variable_names;
-
-
-template<class D>
-bool first_type_is_any()
-{
- return false;
-}
-
-template<class D, class F, class... R>
-constexpr
-bool first_type_is_any()
-{
- return std::is_same<D, F>::value || first_type_is_any<D, R...>();
-}
-
-
-static
-str_data_t *search_strp(XString p)
-{
- return str_datam.search(p);
-}
-
-static
-str_data_t *add_strp(XString p)
-{
- if (str_data_t *rv = search_strp(p))
- return rv;
-
- RString p2 = p;
- str_data_t *datum = str_datam.init(p2);
- datum->type = StringCode::NOP;
- datum->strs = p2;
- datum->backpatch = -1;
- datum->label_ = -1;
- return datum;
-}
-
-/*==========================================
- * スクリプトバッファに1バイト書き込む
- *------------------------------------------
- */
-void ScriptBuffer::add_scriptc(ByteCode a)
-{
- script_buf.push_back(a);
-}
-
-/*==========================================
- * スクリプトバッファにデータタイプを書き込む
- *------------------------------------------
- */
-void ScriptBuffer::add_scriptb(uint8_t a)
-{
- add_scriptc(static_cast<ByteCode>(a));
-}
-
-/*==========================================
- * スクリプトバッファに整数を書き込む
- *------------------------------------------
- */
-void ScriptBuffer::add_scripti(uint32_t a)
-{
- while (a >= 0x40)
- {
- add_scriptb(a | 0xc0);
- a = (a - 0x40) >> 6;
- }
- add_scriptb(a | 0x80);
-}
-
-/*==========================================
- * スクリプトバッファにラベル/変数/関数を書き込む
- *------------------------------------------
- */
-// 最大16Mまで
-void ScriptBuffer::add_scriptl(str_data_t *ld)
-{
- int backpatch = ld->backpatch;
-
- switch (ld->type)
- {
- case StringCode::POS:
- add_scriptc(ByteCode::POS);
- add_scriptb(static_cast<uint8_t>(ld->label_));
- add_scriptb(static_cast<uint8_t>(ld->label_ >> 8));
- add_scriptb(static_cast<uint8_t>(ld->label_ >> 16));
- break;
- case StringCode::NOP:
- // need to set backpatch, because it might become a label later
- add_scriptc(ByteCode::VARIABLE);
- ld->backpatch = script_buf.size();
- add_scriptb(static_cast<uint8_t>(backpatch));
- add_scriptb(static_cast<uint8_t>(backpatch >> 8));
- add_scriptb(static_cast<uint8_t>(backpatch >> 16));
- break;
- case StringCode::INT:
- add_scripti(ld->val);
- break;
- case StringCode::FUNC:
- add_scriptc(ByteCode::FUNC_REF);
- add_scriptb(static_cast<uint8_t>(ld->val));
- add_scriptb(static_cast<uint8_t>(ld->val >> 8));
- add_scriptb(static_cast<uint8_t>(ld->val >> 16));
- break;
- case StringCode::PARAM:
- add_scriptc(ByteCode::PARAM);
- add_scriptb(static_cast<uint8_t>(ld->val));
- add_scriptb(static_cast<uint8_t>(ld->val >> 8));
- add_scriptb(static_cast<uint8_t>(ld->val >> 16));
- break;
- default:
- abort();
- }
-}
-
-/*==========================================
- * ラベルを解決する
- *------------------------------------------
- */
-void ScriptBuffer::set_label(str_data_t *ld, int pos_)
-{
- int next;
-
- ld->type = StringCode::POS;
- ld->label_ = pos_;
- for (int i = ld->backpatch; i >= 0 && i != 0x00ffffff; i = next)
- {
- next = 0;
- // woot! no longer endian-dependent!
- next |= static_cast<uint8_t>(script_buf[i + 0]) << 0;
- next |= static_cast<uint8_t>(script_buf[i + 1]) << 8;
- next |= static_cast<uint8_t>(script_buf[i + 2]) << 16;
- script_buf[i - 1] = ByteCode::POS;
- script_buf[i] = static_cast<ByteCode>(pos_);
- script_buf[i + 1] = static_cast<ByteCode>(pos_ >> 8);
- script_buf[i + 2] = static_cast<ByteCode>(pos_ >> 16);
- }
-}
-
-/*==========================================
- * スペース/コメント読み飛ばし
- *------------------------------------------
- */
-static
-ZString::iterator skip_space(ZString::iterator p)
-{
- while (1)
- {
- while (isspace(*p))
- p++;
- if (p[0] == '/' && p[1] == '/')
- {
- while (*p && *p != '\n')
- p++;
- }
- else if (p[0] == '/' && p[1] == '*')
- {
- p++;
- while (*p && (p[-1] != '*' || p[0] != '/'))
- p++;
- if (*p)
- p++;
- }
- else
- break;
- }
- return p;
-}
-
-/*==========================================
- * 1単語スキップ
- *------------------------------------------
- */
-static
-ZString::iterator skip_word(ZString::iterator p)
-{
- // prefix
- if (*p == '$')
- p++; // MAP鯖内共有変数用
- if (*p == '@')
- p++; // 一時的変数用(like weiss)
- if (*p == '#')
- p++; // account変数用
- if (*p == '#')
- p++; // ワールドaccount変数用
-
- while (isalnum(*p) || *p == '_')
- p++;
-
- // postfix
- if (*p == '$')
- p++; // 文字列変数
-
- return p;
-}
-
-// TODO: replace this whole mess with some sort of input stream that works
-// a line at a time.
-static
-ZString startptr;
-static
-int startline;
-
-int script_errors = 0;
-/*==========================================
- * エラーメッセージ出力
- *------------------------------------------
- */
-static
-void disp_error_message(ZString mes, ZString::iterator pos_)
-{
- script_errors++;
-
- assert (startptr.begin() <= pos_ && pos_ <= startptr.end());
-
- int line;
- ZString::iterator p;
-
- for (line = startline, p = startptr.begin(); p != startptr.end(); line++)
- {
- ZString::iterator linestart = p;
- ZString::iterator lineend = std::find(p, startptr.end(), '\n');
- if (pos_ < lineend)
- {
- PRINTF("\n%s\nline %d : "_fmt, mes, line);
- for (int i = 0; linestart + i != lineend; i++)
- {
- if (linestart + i != pos_)
- PRINTF("%c"_fmt, linestart[i]);
- else
- PRINTF("\'%c\'"_fmt, linestart[i]);
- }
- PRINTF("\a\n"_fmt);
- return;
- }
- p = lineend + 1;
- }
-}
-
-/*==========================================
- * 項の解析
- *------------------------------------------
- */
-ZString::iterator ScriptBuffer::parse_simpleexpr(ZString::iterator p)
-{
- p = skip_space(p);
-
- if (*p == ';' || *p == ',')
- {
- disp_error_message("unexpected expr end"_s, p);
- exit(1);
- }
- if (*p == '(')
- {
-
- p = parse_subexpr(p + 1, -1);
- p = skip_space(p);
- if ((*p++) != ')')
- {
- disp_error_message("unmatch ')'"_s, p);
- exit(1);
- }
- }
- else if (isdigit(*p) || ((*p == '-' || *p == '+') && isdigit(p[1])))
- {
- char *np;
- int i = strtoul(&*p, &np, 0);
- add_scripti(i);
- p += np - &*p;
- }
- else if (*p == '"')
- {
- add_scriptc(ByteCode::STR);
- p++;
- while (*p && *p != '"')
- {
- if (*p == '\\')
- p++;
- else if (*p == '\n')
- {
- disp_error_message("unexpected newline @ string"_s, p);
- exit(1);
- }
- add_scriptb(*p++);
- }
- if (!*p)
- {
- disp_error_message("unexpected eof @ string"_s, p);
- exit(1);
- }
- add_scriptb(0);
- p++; //'"'
- }
- else
- {
- // label , register , function etc
- ZString::iterator p2 = skip_word(p);
- if (p2 == p)
- {
- disp_error_message("unexpected character"_s, p);
- exit(1);
- }
- XString word(&*p, &*p2, nullptr);
- if (word.startswith("On"_s) || word.startswith("L_"_s) || word.startswith("S_"_s))
- probable_labels.insert(stringish<ScriptLabel>(word));
- if (parse_cmd_if && (word == "callsub"_s || word == "callfunc"_s || word == "return"_s))
- {
- disp_error_message("Sorry, callsub/callfunc/return have never worked properly in an if statement."_s, p);
- }
- str_data_t *ld = add_strp(word);
-
- parse_cmdp = ld; // warn_*_mismatch_paramnumのために必要
- // why not just check l->str == "if"_s or std::string(p, p2) == "if"_s?
- if (ld == search_strp("if"_s)) // warn_cmd_no_commaのために必要
- parse_cmd_if++;
- p = p2;
-
- if (ld->type != StringCode::FUNC && *p == '[')
- {
- // array(name[i] => getelementofarray(name,i) )
- add_scriptl(search_strp("getelementofarray"_s));
- add_scriptc(ByteCode::ARG);
- add_scriptl(ld);
- p = parse_subexpr(p + 1, -1);
- p = skip_space(p);
- if (*p != ']')
- {
- disp_error_message("unmatch ']'"_s, p);
- exit(1);
- }
- p++;
- add_scriptc(ByteCode::FUNC);
- }
- else
- add_scriptl(ld);
-
- }
-
- return p;
-}
-
-/*==========================================
- * 式の解析
- *------------------------------------------
- */
-ZString::iterator ScriptBuffer::parse_subexpr(ZString::iterator p, int limit)
-{
- ByteCode op;
- int opl, len;
-
- p = skip_space(p);
-
- if (*p == '-')
- {
- ZString::iterator tmpp = skip_space(p + 1);
- if (*tmpp == ';' || *tmpp == ',')
- {
- --script_errors; disp_error_message("deprecated: implicit 'next statement' label"_s, p);
- add_scriptl(&LABEL_NEXTLINE_);
- p++;
- return p;
- }
- }
- ZString::iterator tmpp = p;
- if ((op = ByteCode::NEG, *p == '-') || (op = ByteCode::LNOT, *p == '!')
- || (op = ByteCode::NOT, *p == '~'))
- {
- p = parse_subexpr(p + 1, 100);
- add_scriptc(op);
- }
- else
- p = parse_simpleexpr(p);
- p = skip_space(p);
- while (((op = ByteCode::ADD, opl = 6, len = 1, *p == '+') ||
- (op = ByteCode::SUB, opl = 6, len = 1, *p == '-') ||
- (op = ByteCode::MUL, opl = 7, len = 1, *p == '*') ||
- (op = ByteCode::DIV, opl = 7, len = 1, *p == '/') ||
- (op = ByteCode::MOD, opl = 7, len = 1, *p == '%') ||
- (op = ByteCode::FUNC, opl = 8, len = 1, *p == '(') ||
- (op = ByteCode::LAND, opl = 1, len = 2, *p == '&' && p[1] == '&') ||
- (op = ByteCode::AND, opl = 5, len = 1, *p == '&') ||
- (op = ByteCode::LOR, opl = 0, len = 2, *p == '|' && p[1] == '|') ||
- (op = ByteCode::OR, opl = 4, len = 1, *p == '|') ||
- (op = ByteCode::XOR, opl = 3, len = 1, *p == '^') ||
- (op = ByteCode::EQ, opl = 2, len = 2, *p == '=' && p[1] == '=') ||
- (op = ByteCode::NE, opl = 2, len = 2, *p == '!' && p[1] == '=') ||
- (op = ByteCode::R_SHIFT, opl = 5, len = 2, *p == '>' && p[1] == '>') ||
- (op = ByteCode::GE, opl = 2, len = 2, *p == '>' && p[1] == '=') ||
- (op = ByteCode::GT, opl = 2, len = 1, *p == '>') ||
- (op = ByteCode::L_SHIFT, opl = 5, len = 2, *p == '<' && p[1] == '<') ||
- (op = ByteCode::LE, opl = 2, len = 2, *p == '<' && p[1] == '=') ||
- (op = ByteCode::LT, opl = 2, len = 1, *p == '<')) && opl > limit)
- {
- p += len;
- if (op == ByteCode::FUNC)
- {
- int i = 0;
- str_data_t *funcp = parse_cmdp;
- ZString::iterator plist[128];
-
- if (funcp->type != StringCode::FUNC)
- {
- disp_error_message("expect function"_s, tmpp);
- exit(0);
- }
-
- add_scriptc(ByteCode::ARG);
- while (*p && *p != ')' && i < 128)
- {
- plist[i] = p;
- p = parse_subexpr(p, -1);
- p = skip_space(p);
- if (*p == ',')
- p++;
- else if (*p != ')' && script_config.warn_func_no_comma)
- {
- disp_error_message("expect ',' or ')' at func params"_s,
- p);
- }
- p = skip_space(p);
- i++;
- }
- plist[i] = p;
- if (*p != ')')
- {
- disp_error_message("func request '(' ')'"_s, p);
- exit(1);
- }
- p++;
-
- if (funcp->type == StringCode::FUNC
- && script_config.warn_func_mismatch_paramnum)
- {
- ZString arg = builtin_functions[funcp->val].arg;
- int j = 0;
- // TODO handle ? and multiple * correctly
- for (j = 0; arg[j]; j++)
- if (arg[j] == '*' || arg[j] == '?')
- break;
- if ((arg[j] == 0 && i != j) || ((arg[j] == '*' || arg[j] == '?') && i < j))
- {
- disp_error_message("illegal number of parameters"_s,
- plist[std::min(i, j)]);
- }
- if (!builtin_functions[funcp->val].ret)
- {
- disp_error_message("statement in function context"_s, tmpp);
- }
- }
- }
- else // not op == ByteCode::FUNC
- {
- p = parse_subexpr(p, opl);
- }
- add_scriptc(op);
- p = skip_space(p);
- }
- return p; /* return first untreated operator */
-}
-
-/*==========================================
- * 式の評価
- *------------------------------------------
- */
-ZString::iterator ScriptBuffer::parse_expr(ZString::iterator p)
-{
- switch (*p)
- {
- case ')':
- case ';':
- case ':':
- case '[':
- case ']':
- case '}':
- disp_error_message("unexpected char"_s, p);
- exit(1);
- }
- p = parse_subexpr(p, -1);
- return p;
-}
-
-/*==========================================
- * 行の解析
- *------------------------------------------
- */
-ZString::iterator ScriptBuffer::parse_line(ZString::iterator p, bool *can_step)
-{
- int i = 0;
- ZString::iterator plist[128];
-
- p = skip_space(p);
- if (*p == ';')
- return p;
-
- parse_cmd_if = 0; // warn_cmd_no_commaのために必要
-
- // 最初は関数名
- ZString::iterator p2 = p;
- p = parse_simpleexpr(p);
- p = skip_space(p);
-
- str_data_t *cmd = parse_cmdp;
- if (cmd->type != StringCode::FUNC)
- {
- disp_error_message("expect command"_s, p2);
- }
-
- {
- // TODO should be LString, but no heterogenous lookup yet
- static
- std::set<ZString> terminators =
- {
- "goto"_s,
- "return"_s,
- "close"_s,
- "menu"_s,
- "end"_s,
- "mapexit"_s,
- "shop"_s,
- };
- *can_step = terminators.count(cmd->strs) == 0;
- }
-
- add_scriptc(ByteCode::ARG);
- while (*p && *p != ';' && i < 128)
- {
- plist[i] = p;
-
- p = parse_expr(p);
- p = skip_space(p);
- // 引数区切りの,処理
- if (*p == ',')
- p++;
- else if (*p != ';' && script_config.warn_cmd_no_comma
- && parse_cmd_if * 2 <= i)
- {
- disp_error_message("expect ',' or ';' at cmd params"_s, p);
- }
- p = skip_space(p);
- i++;
- }
- plist[i] = p;
- if (*(p++) != ';')
- {
- disp_error_message("need ';'"_s, p);
- exit(1);
- }
- add_scriptc(ByteCode::FUNC);
-
- if (cmd->type == StringCode::FUNC
- && script_config.warn_cmd_mismatch_paramnum)
- {
- ZString arg = builtin_functions[cmd->val].arg;
- int j = 0;
- // TODO see above
- for (j = 0; arg[j]; j++)
- if (arg[j] == '*' || arg[j] == '?')
- break;
- if ((arg[j] == 0 && i != j) || ((arg[j] == '*' || arg[j] == '?') && i < j))
- {
- disp_error_message("illegal number of parameters"_s,
- plist[std::min(i, j)]);
- }
- if (builtin_functions[cmd->val].ret)
- {
- disp_error_message("function in statement context"_s, p2);
- }
- }
-
- return p;
-}
-
-/*==========================================
- * 組み込み関数の追加
- *------------------------------------------
- */
-static
-void add_builtin_functions(void)
-{
- for (int i = 0; builtin_functions[i].func; i++)
- {
- str_data_t *n = add_strp(builtin_functions[i].name);
- n->type = StringCode::FUNC;
- n->val = i;
- }
-}
-
-bool read_constdb(ZString filename)
-{
- io::ReadFile in(filename);
- if (!in.is_open())
- {
- PRINTF("can't read %s\n"_fmt, filename);
- return false;
- }
-
- bool rv = true;
- AString line_;
- while (in.getline(line_))
- {
- // is_comment only works for whole-line comments
- // that could change once the Z dependency is dropped ...
- LString comment = "//"_s;
- XString line = line_.xislice_h(std::search(line_.begin(), line_.end(), comment.begin(), comment.end())).rstrip();
- if (!line)
- continue;
- // "%m[A-Za-z0-9_] %i %i"
-
- // TODO promote either qsplit() or asplit()
- auto _it = std::find(line.begin(), line.end(), ' ');
- auto name = line.xislice_h(_it);
- auto _rest = line.xislice_t(_it);
- while (_rest.startswith(' '))
- _rest = _rest.xslice_t(1);
- auto _it2 = std::find(_rest.begin(), _rest.end(), ' ');
- auto val_ = _rest.xislice_h(_it2);
- auto type_ = _rest.xislice_t(_it2);
- while (type_.startswith(' '))
- type_ = type_.xslice_t(1);
- // yes, the above actually DTRT even for underlength input
-
- int val;
- int type = 0;
- // Note for future archeaologists: this code is indented correctly
- if (std::find_if_not(name.begin(), name.end(),
- [](char c)
- {
- return ('0' <= c && c <= '9')
- || ('A' <= c && c <= 'Z')
- || ('a' <= c && c <= 'z')
- || (c == '_');
- }) != name.end()
- || !extract(val_, &val)
- || (!extract(type_, &type) && type_))
- {
- PRINTF("Bad const line: %s\n"_fmt, line_);
- rv = false;
- continue;
- }
- str_data_t *n = add_strp(name);
- n->type = type ? StringCode::PARAM : StringCode::INT;
- n->val = val;
- }
- return rv;
-}
-
-std::unique_ptr<const ScriptBuffer> parse_script(ZString src, int line, bool implicit_end)
-{
- auto script_buf = make_unique<ScriptBuffer>();
- script_buf->parse_script(src, line, implicit_end);
- return std::move(script_buf);
-}
-
-/*==========================================
- * スクリプトの解析
- *------------------------------------------
- */
-void ScriptBuffer::parse_script(ZString src, int line, bool implicit_end)
-{
- static int first = 1;
-
- if (first)
- {
- add_builtin_functions();
- }
- first = 0;
- LABEL_NEXTLINE_.type = StringCode::NOP;
- LABEL_NEXTLINE_.backpatch = -1;
- LABEL_NEXTLINE_.label_ = -1;
- for (auto& pair : str_datam)
- {
- str_data_t& dit = pair.second;
- if (dit.type == StringCode::POS || dit.type == StringCode::VARIABLE)
- {
- dit.type = StringCode::NOP;
- dit.backpatch = -1;
- dit.label_ = -1;
- }
- }
-
- // 外部用label dbの初期化
- scriptlabel_db.clear();
-
- // for error message
- startptr = src;
- startline = line;
-
- bool can_step = true;
-
- ZString::iterator p = src.begin();
- p = skip_space(p);
- if (*p != '{')
- {
- disp_error_message("not found '{'"_s, p);
- abort();
- }
- for (p++; *p && *p != '}';)
- {
- p = skip_space(p);
- if (*skip_space(skip_word(p)) == ':')
- {
- if (can_step)
- {
- --script_errors; disp_error_message("deprecated: implicit fallthrough"_s, p);
- }
- can_step = true;
-
- ZString::iterator tmpp = skip_word(p);
- XString str(&*p, &*tmpp, nullptr);
- str_data_t *ld = add_strp(str);
- bool e1 = ld->type != StringCode::NOP;
- bool e2 = ld->type == StringCode::POS;
- bool e3 = ld->label_ != -1;
- assert (e1 == e2 && e2 == e3);
- if (e3)
- {
- disp_error_message("dup label "_s, p);
- exit(1);
- }
- set_label(ld, script_buf.size());
- scriptlabel_db.insert(stringish<ScriptLabel>(str), script_buf.size());
- p = tmpp + 1;
- continue;
- }
-
- if (!can_step)
- {
- --script_errors; disp_error_message("deprecated: unreachable statement"_s, p);
- }
- // 他は全部一緒くた
- p = parse_line(p, &can_step);
- p = skip_space(p);
- add_scriptc(ByteCode::EOL);
-
- set_label(&LABEL_NEXTLINE_, script_buf.size());
- LABEL_NEXTLINE_.type = StringCode::NOP;
- LABEL_NEXTLINE_.backpatch = -1;
- LABEL_NEXTLINE_.label_ = -1;
- }
-
- if (can_step && !implicit_end)
- {
- --script_errors; disp_error_message("deprecated: implicit end"_s, p);
- }
- add_scriptc(ByteCode::NOP);
-
- // resolve the unknown labels
- for (auto& pair : str_datam)
- {
- str_data_t& sit = pair.second;
- if (sit.type == StringCode::NOP)
- {
- sit.type = StringCode::VARIABLE;
- sit.label_ = 0; // anything but -1. Shouldn't matter, but helps asserts.
- size_t pool_index = variable_names.intern(sit.strs);
- for (int next, j = sit.backpatch; j >= 0 && j != 0x00ffffff; j = next)
- {
- next = 0;
- next |= static_cast<uint8_t>(script_buf[j + 0]) << 0;
- next |= static_cast<uint8_t>(script_buf[j + 1]) << 8;
- next |= static_cast<uint8_t>(script_buf[j + 2]) << 16;
- script_buf[j] = static_cast<ByteCode>(pool_index);
- script_buf[j + 1] = static_cast<ByteCode>(pool_index >> 8);
- script_buf[j + 2] = static_cast<ByteCode>(pool_index >> 16);
- }
- }
- }
-
- for (const auto& pair : scriptlabel_db)
- {
- ScriptLabel key = pair.first;
- if (key.startswith("On"_s))
- continue;
- if (!(key.startswith("L_"_s) || key.startswith("S_"_s)))
- PRINTF("Warning: ugly label: %s\n"_fmt, key);
- else if (!probable_labels.count(key))
- PRINTF("Warning: unused label: %s\n"_fmt, key);
- }
- for (ScriptLabel used : probable_labels)
- {
- if (!scriptlabel_db.search(used))
- PRINTF("Warning: no such label: %s\n"_fmt, used);
- }
- probable_labels.clear();
-
- if (!DEBUG_DISP)
- return;
- for (size_t i = 0; i < script_buf.size(); i++)
- {
- if ((i & 15) == 0)
- PRINTF("%04zx : "_fmt, i);
- PRINTF("%02x "_fmt, script_buf[i]);
- if ((i & 15) == 15)
- PRINTF("\n"_fmt);
- }
- PRINTF("\n"_fmt);
-}
-
-//
-// 実行系
-//
-enum class ScriptEndState
-{
- ZERO,
- STOP,
- END,
- RERUNLINE,
- GOTO,
- RETFUNC,
-};
-
-/*==========================================
- * ridからsdへの解決
- *------------------------------------------
- */
-static
-dumb_ptr<map_session_data> script_rid2sd(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = map_id2sd(st->rid);
- if (!sd)
- {
- PRINTF("script_rid2sd: fatal error ! player not attached!\n"_fmt);
- }
- return sd;
-}
-
-/*==========================================
- * 変数の読み取り
- *------------------------------------------
- */
-static
-void get_val(dumb_ptr<map_session_data> sd, struct script_data *data)
-{
- MATCH (*data)
- {
- CASE (const ScriptDataParam&, u)
- {
- if (sd == nullptr)
- PRINTF("get_val error param SP::%d\n"_fmt, u.reg.sp());
- int numi = 0;
- if (sd)
- numi = pc_readparam(sd, u.reg.sp());
- *data = ScriptDataInt{numi};
- }
- CASE (const ScriptDataVariable&, u)
- {
- ZString name_ = variable_names.outtern(u.reg.base());
- VarName name = stringish<VarName>(name_);
- char prefix = name.front();
- char postfix = name.back();
-
- if (prefix != '$')
- {
- if (sd == nullptr)
- PRINTF("get_val error name?:%s\n"_fmt, name);
- }
- if (postfix == '$')
- {
- RString str;
- if (prefix == '@')
- {
- if (sd)
- str = pc_readregstr(sd, u.reg);
- }
- else if (prefix == '$')
- {
- RString *s = mapregstr_db.search(u.reg);
- if (s)
- str = *s;
- }
- else
- {
- PRINTF("script: get_val: illegal scope string variable.\n"_fmt);
- str = "!!ERROR!!"_s;
- }
- *data = ScriptDataStr{str};
- }
- else
- {
- int numi = 0;
- if (prefix == '@')
- {
- if (sd)
- numi = pc_readreg(sd, u.reg);
- }
- else if (prefix == '$')
- {
- numi = mapreg_db.get(u.reg);
- }
- else if (prefix == '#')
- {
- if (name[1] == '#')
- {
- if (sd)
- numi = pc_readaccountreg2(sd, name);
- }
- else
- {
- if (sd)
- numi = pc_readaccountreg(sd, name);
- }
- }
- else
- {
- if (sd)
- numi = pc_readglobalreg(sd, name);
- }
- *data = ScriptDataInt{numi};
- }
- }
- }
-}
-
-static __attribute__((deprecated))
-void get_val(ScriptState *st, struct script_data *data)
-{
- dumb_ptr<map_session_data> sd = st->rid ? map_id2sd(st->rid) : nullptr;
- get_val(sd, data);
-}
-
-/*==========================================
- * 変数の読み取り2
- *------------------------------------------
- */
-static
-struct script_data get_val2(ScriptState *st, SIR reg)
-{
- struct script_data dat = ScriptDataVariable{reg};
- get_val(st, &dat);
- return dat;
-}
-
-/*==========================================
- * 変数設定用
- *------------------------------------------
- */
-static
-void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, struct script_data vd)
-{
- if (type == VariableCode::PARAM)
- {
- int val = vd.get_if<ScriptDataInt>()->numi;
- pc_setparam(sd, reg.sp(), val);
- return;
- }
- assert (type == VariableCode::VARIABLE);
-
- ZString name_ = variable_names.outtern(reg.base());
- VarName name = stringish<VarName>(name_);
- char prefix = name.front();
- char postfix = name.back();
-
- if (postfix == '$')
- {
- RString str = vd.get_if<ScriptDataStr>()->str;
- if (prefix == '@')
- {
- pc_setregstr(sd, reg, str);
- }
- else if (prefix == '$')
- {
- mapreg_setregstr(reg, str);
- }
- else
- {
- PRINTF("script: set_reg: illegal scope string variable !"_fmt);
- }
- }
- else
- {
- int val = vd.get_if<ScriptDataInt>()->numi;
- if (prefix == '@')
- {
- pc_setreg(sd, reg, val);
- }
- else if (prefix == '$')
- {
- mapreg_setreg(reg, val);
- }
- else if (prefix == '#')
- {
- if (name[1] == '#')
- pc_setaccountreg2(sd, name, val);
- else
- pc_setaccountreg(sd, name, val);
- }
- else
- {
- pc_setglobalreg(sd, name, val);
- }
- }
-}
-
-static
-void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, int id)
-{
- struct script_data vd = ScriptDataInt{id};
- set_reg(sd, type, reg, vd);
-}
-
-static
-void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, RString zd)
-{
- struct script_data vd = ScriptDataStr{zd};
- set_reg(sd, type, reg, vd);
-}
-
-/*==========================================
- * 文字列への変換
- *------------------------------------------
- */
-static __attribute__((warn_unused_result))
-RString conv_str(ScriptState *st, struct script_data *data)
-{
- get_val(st, data);
- assert (!data->is<ScriptDataRetInfo>());
- if (auto *u = data->get_if<ScriptDataInt>())
- {
- AString buf = STRPRINTF("%d"_fmt, u->numi);
- *data = ScriptDataStr{buf};
- }
- return data->get_if<ScriptDataStr>()->str;
-}
-
-/*==========================================
- * 数値へ変換
- *------------------------------------------
- */
-static __attribute__((warn_unused_result))
-int conv_num(ScriptState *st, struct script_data *data)
-{
- int rv = 0;
- get_val(st, data);
- assert (!data->is<ScriptDataRetInfo>());
- MATCH (*data)
- {
- default:
- abort();
- CASE (const ScriptDataStr&, u)
- {
- RString p = u.str;
- rv = atoi(p.c_str());
- }
- CASE (const ScriptDataInt&, u)
- {
- return u.numi;
- }
- CASE (const ScriptDataPos&, u)
- {
- return u.numi;
- }
- }
- *data = ScriptDataInt{rv};
- return rv;
-}
-
-static __attribute__((warn_unused_result))
-const ScriptBuffer *conv_script(ScriptState *st, struct script_data *data)
-{
- get_val(st, data);
- return data->get_if<ScriptDataRetInfo>()->script;
-}
-
-
-template<class T>
-static
-void push_int(struct script_stack *stack, int val)
-{
- static_assert(first_type_is_any<T, ScriptDataPos, ScriptDataInt, ScriptDataArg, ScriptDataFuncRef>(), "not int type");
-
- script_data nsd = T{.numi= val};
- stack->stack_datav.push_back(nsd);
-}
-
-template<class T>
-static
-void push_reg(struct script_stack *stack, SIR reg)
-{
- static_assert(first_type_is_any<T, ScriptDataParam, ScriptDataVariable>(), "not reg type");
-
- script_data nsd = T{.reg= reg};
- stack->stack_datav.push_back(nsd);
-}
-
-template<class T>
-static
-void push_script(struct script_stack *stack, const ScriptBuffer *code)
-{
- static_assert(first_type_is_any<T, ScriptDataRetInfo>(), "not scriptbuf type");
-
- script_data nsd = T{.script= code};
- stack->stack_datav.push_back(nsd);
-}
-
-template<class T>
-static
-void push_str(struct script_stack *stack, RString str)
-{
- static_assert(first_type_is_any<T, ScriptDataStr>(), "not str type");
-
- script_data nsd = T{.str= str};
- stack->stack_datav.push_back(nsd);
-}
-
-static
-void push_copy(struct script_stack *stack, int pos_)
-{
- script_data csd = stack->stack_datav[pos_];
- stack->stack_datav.push_back(csd);
-}
-
-static
-void pop_stack(struct script_stack *stack, int start, int end)
-{
- auto it = stack->stack_datav.begin();
- stack->stack_datav.erase(it + start, it + end);
-}
-
-
-#define AARGO2(n) (st->stack->stack_datav[st->start + (n)])
-#define HARGO2(n) (st->end > st->start + (n))
-
-//
-// 埋め込み関数
-//
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_mes(ScriptState *st)
-{
- RString mes = conv_str(st, &AARGO2(2));
- clif_scriptmes(script_rid2sd(st), st->oid, mes);
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_goto(ScriptState *st)
-{
- if (!AARGO2(2).is<ScriptDataPos>())
- {
- PRINTF("script: goto: not label !\n"_fmt);
- st->state = ScriptEndState::END;
- return;
- }
-
- st->scriptp.pos = conv_num(st, &AARGO2(2));
- st->state = ScriptEndState::GOTO;
-}
-
-/*==========================================
- * ユーザー定義関数の呼び出し
- *------------------------------------------
- */
-static
-void builtin_callfunc(ScriptState *st)
-{
- RString str = conv_str(st, &AARGO2(2));
- const ScriptBuffer *scr = userfunc_db.get(str);
-
- if (scr)
- {
- int j = 0;
- assert (st->start + 3 == st->end);
-#if 0
- for (int i = st->start + 3; i < st->end; i++, j++)
- push_copy(st->stack, i);
-#endif
-
- push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ
- push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ
- push_int<ScriptDataInt>(st->stack, st->scriptp.pos); // 現在のスクリプト位置をプッシュ
- push_script<ScriptDataRetInfo>(st->stack, st->scriptp.code); // 現在のスクリプトをプッシュ
-
- st->scriptp = ScriptPointer(scr, 0);
- st->defsp = st->start + 4 + j;
- st->state = ScriptEndState::GOTO;
- }
- else
- {
- PRINTF("script:callfunc: function not found! [%s]\n"_fmt, str);
- st->state = ScriptEndState::END;
- }
-}
-
-/*==========================================
- * サブルーティンの呼び出し
- *------------------------------------------
- */
-static
-void builtin_callsub(ScriptState *st)
-{
- int pos_ = conv_num(st, &AARGO2(2));
- int j = 0;
- assert (st->start + 3 == st->end);
-#if 0
- for (int i = st->start + 3; i < st->end; i++, j++)
- push_copy(st->stack, i);
-#endif
-
- push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ
- push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ
- push_int<ScriptDataInt>(st->stack, st->scriptp.pos); // 現在のスクリプト位置をプッシュ
- push_script<ScriptDataRetInfo>(st->stack, st->scriptp.code); // 現在のスクリプトをプッシュ
-
- st->scriptp.pos = pos_;
- st->defsp = st->start + 4 + j;
- st->state = ScriptEndState::GOTO;
-}
-
-/*==========================================
- * サブルーチン/ユーザー定義関数の終了
- *------------------------------------------
- */
-static
-void builtin_return(ScriptState *st)
-{
-#if 0
- if (HARGO2(2))
- { // 戻り値有り
- push_copy(st->stack, st->start + 2);
- }
-#endif
- st->state = ScriptEndState::RETFUNC;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_next(ScriptState *st)
-{
- st->state = ScriptEndState::STOP;
- clif_scriptnext(script_rid2sd(st), st->oid);
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_close(ScriptState *st)
-{
- st->state = ScriptEndState::END;
- clif_scriptclose(script_rid2sd(st), st->oid);
-}
-
-static
-void builtin_close2(ScriptState *st)
-{
- st->state = ScriptEndState::STOP;
- clif_scriptclose(script_rid2sd(st), st->oid);
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_menu(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- if (sd->state.menu_or_input == 0)
- {
- // First half: show menu.
- st->state = ScriptEndState::RERUNLINE;
- sd->state.menu_or_input = 1;
-
- MString buf;
- for (int i = st->start + 2; i < st->end; i += 2)
- {
- RString choice_str = conv_str(st, &AARGO2(i - st->start));
- if (!choice_str)
- break;
- buf += choice_str;
- buf += ':';
- }
-
- clif_scriptmenu(script_rid2sd(st), st->oid, AString(buf));
- }
- else
- {
- // Rerun: item is chosen from menu.
- if (sd->npc_menu == 0xff)
- {
- // cancel
- sd->state.menu_or_input = 0;
- st->state = ScriptEndState::END;
- return;
- }
-
- // Actually jump to the label.
- // Logic change: menu_choices is the *total* number of labels,
- // not just the displayed number that ends with the "".
- // (Would it be better to pop the stack before rerunning?)
- int menu_choices = (st->end - (st->start + 2)) / 2;
- pc_setreg(sd, SIR::from(variable_names.intern("@menu"_s)), sd->npc_menu);
- sd->state.menu_or_input = 0;
- if (sd->npc_menu > 0 && sd->npc_menu <= menu_choices)
- {
- int arg_index = (sd->npc_menu - 1) * 2 + 1;
- if (!AARGO2(arg_index + 2).is<ScriptDataPos>())
- {
- st->state = ScriptEndState::END;
- return;
- }
- st->scriptp.pos = AARGO2(arg_index + 2).get_if<ScriptDataPos>()->numi;
- st->state = ScriptEndState::GOTO;
- }
- }
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_rand(ScriptState *st)
-{
- if (HARGO2(3))
- {
- int min = conv_num(st, &AARGO2(2));
- int max = conv_num(st, &AARGO2(3));
- if (min > max)
- std::swap(max, min);
- push_int<ScriptDataInt>(st->stack, random_::in(min, max));
- }
- else
- {
- int range = conv_num(st, &AARGO2(2));
- push_int<ScriptDataInt>(st->stack, range <= 0 ? 0 : random_::to(range));
- }
-}
-
-/*==========================================
- * Check whether the PC is at the specified location
- *------------------------------------------
- */
-static
-void builtin_isat(ScriptState *st)
-{
- int x, y;
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- x = conv_num(st, &AARGO2(3));
- y = conv_num(st, &AARGO2(4));
-
- if (!sd)
- return;
-
- push_int<ScriptDataInt>(st->stack,
- (x == sd->bl_x) && (y == sd->bl_y)
- && (str == sd->bl_m->name_));
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_warp(ScriptState *st)
-{
- int x, y;
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- x = conv_num(st, &AARGO2(3));
- y = conv_num(st, &AARGO2(4));
- if (str == "Random"_s)
- pc_randomwarp(sd, BeingRemoveWhy::WARPED);
- else if (str == "SavePoint"_s or str == "Save"_s)
- {
- if (sd->bl_m->flag.get(MapFlag::NORETURN))
- return;
-
- pc_setpos(sd, sd->status.save_point.map_, sd->status.save_point.x, sd->status.save_point.y,
- BeingRemoveWhy::WARPED);
- }
- else
- pc_setpos(sd, str, x, y, BeingRemoveWhy::GONE);
-}
-
-/*==========================================
- * エリア指定ワープ
- *------------------------------------------
- */
-static
-void builtin_areawarp_sub(dumb_ptr<block_list> bl, MapName mapname, int x, int y)
-{
- dumb_ptr<map_session_data> sd = bl->is_player();
- if (mapname == "Random"_s)
- pc_randomwarp(sd, BeingRemoveWhy::WARPED);
- else
- pc_setpos(sd, mapname, x, y, BeingRemoveWhy::GONE);
-}
-
-static
-void builtin_areawarp(ScriptState *st)
-{
- int x, y;
- int x0, y0, x1, y1;
-
- MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- x0 = conv_num(st, &AARGO2(3));
- y0 = conv_num(st, &AARGO2(4));
- x1 = conv_num(st, &AARGO2(5));
- y1 = conv_num(st, &AARGO2(6));
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(7))));
- x = conv_num(st, &AARGO2(8));
- y = conv_num(st, &AARGO2(9));
-
- map_local *m = map_mapname2mapid(mapname);
- if (m == nullptr)
- return;
-
- map_foreachinarea(std::bind(builtin_areawarp_sub, ph::_1, str, x, y),
- m,
- x0, y0,
- x1, y1,
- BL::PC);
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_heal(ScriptState *st)
-{
- int hp, sp;
-
- hp = conv_num(st, &AARGO2(2));
- sp = conv_num(st, &AARGO2(3));
- pc_heal(script_rid2sd(st), hp, sp);
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_itemheal(ScriptState *st)
-{
- int hp, sp;
-
- hp = conv_num(st, &AARGO2(2));
- sp = conv_num(st, &AARGO2(3));
- pc_itemheal(script_rid2sd(st), hp, sp);
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_percentheal(ScriptState *st)
-{
- int hp, sp;
-
- hp = conv_num(st, &AARGO2(2));
- sp = conv_num(st, &AARGO2(3));
- pc_percentheal(script_rid2sd(st), hp, sp);
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_input(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = nullptr;
- script_data& scrd = AARGO2(2);
- assert (scrd.is<ScriptDataVariable>());
-
- SIR reg = scrd.get_if<ScriptDataVariable>()->reg;
- ZString name = variable_names.outtern(reg.base());
-// char prefix = name.front();
- char postfix = name.back();
-
- sd = script_rid2sd(st);
- if (sd->state.menu_or_input)
- {
- // Second time (rerun)
- sd->state.menu_or_input = 0;
- if (postfix == '$')
- {
- set_reg(sd, VariableCode::VARIABLE, reg, sd->npc_str);
- }
- else
- {
- //commented by Lupus (check Value Number Input fix in clif.c)
- //** Fix by fritz :X keeps people from abusing old input bugs
- // wtf?
- if (sd->npc_amount < 0) //** If input amount is less then 0
- {
- clif_tradecancelled(sd); // added "Deal has been cancelled" message by Valaris
- builtin_close(st); //** close
- }
-
- set_reg(sd, VariableCode::VARIABLE, reg, sd->npc_amount);
- }
- }
- else
- {
- // First time - send prompt to client, then wait
- st->state = ScriptEndState::RERUNLINE;
- if (postfix == '$')
- clif_scriptinputstr(sd, st->oid);
- else
- clif_scriptinput(sd, st->oid);
- sd->state.menu_or_input = 1;
- }
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_if (ScriptState *st)
-{
- int sel, i;
-
- sel = conv_num(st, &AARGO2(2));
- if (!sel)
- return;
-
- // 関数名をコピー
- push_copy(st->stack, st->start + 3);
- // 間に引数マーカを入れて
- push_int<ScriptDataArg>(st->stack, 0);
- // 残りの引数をコピー
- for (i = st->start + 4; i < st->end; i++)
- {
- push_copy(st->stack, i);
- }
- run_func(st);
-}
-
-/*==========================================
- * 変数設定
- *------------------------------------------
- */
-static
-void builtin_set(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = nullptr;
- if (auto *u = AARGO2(2).get_if<ScriptDataParam>())
- {
- SIR reg = u->reg;
- sd = script_rid2sd(st);
-
- int val = conv_num(st, &AARGO2(3));
- set_reg(sd, VariableCode::PARAM, reg, val);
- return;
- }
-
- SIR reg = AARGO2(2).get_if<ScriptDataVariable>()->reg;
-
- ZString name = variable_names.outtern(reg.base());
- char prefix = name.front();
- char postfix = name.back();
-
- if (prefix != '$')
- sd = script_rid2sd(st);
-
- if (postfix == '$')
- {
- // 文字列
- RString str = conv_str(st, &AARGO2(3));
- set_reg(sd, VariableCode::VARIABLE, reg, str);
- }
- else
- {
- // 数値
- int val = conv_num(st, &AARGO2(3));
- set_reg(sd, VariableCode::VARIABLE, reg, val);
- }
-
-}
-
-/*==========================================
- * 配列変数設定
- *------------------------------------------
- */
-static
-void builtin_setarray(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = nullptr;
- SIR reg = AARGO2(2).get_if<ScriptDataVariable>()->reg;
- ZString name = variable_names.outtern(reg.base());
- char prefix = name.front();
- char postfix = name.back();
-
- if (prefix != '$' && prefix != '@')
- {
- PRINTF("builtin_setarray: illegal scope !\n"_fmt);
- return;
- }
- if (prefix != '$')
- sd = script_rid2sd(st);
-
- for (int j = 0, i = st->start + 3; i < st->end && j < 256; i++, j++)
- {
- if (postfix == '$')
- set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), conv_str(st, &AARGO2(i - st->start)));
- else
- set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), conv_num(st, &AARGO2(i - st->start)));
- }
-}
-
-/*==========================================
- * 配列変数クリア
- *------------------------------------------
- */
-static
-void builtin_cleararray(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = nullptr;
- SIR reg = AARGO2(2).get_if<ScriptDataVariable>()->reg;
- ZString name = variable_names.outtern(reg.base());
- char prefix = name.front();
- char postfix = name.back();
- int sz = conv_num(st, &AARGO2(4));
-
- if (prefix != '$' && prefix != '@')
- {
- PRINTF("builtin_cleararray: illegal scope !\n"_fmt);
- return;
- }
- if (prefix != '$')
- sd = script_rid2sd(st);
-
- for (int i = 0; i < sz; i++)
- {
- if (postfix == '$')
- set_reg(sd, VariableCode::VARIABLE, reg.iplus(i), conv_str(st, &AARGO2(3)));
- else
- set_reg(sd, VariableCode::VARIABLE, reg.iplus(i), conv_num(st, &AARGO2(3)));
- }
-
-}
-
-/*==========================================
- * 配列変数のサイズ所得
- *------------------------------------------
- */
-static
-int getarraysize(ScriptState *st, SIR reg)
-{
- int i = reg.index(), c = i;
- for (; i < 256; i++)
- {
- struct script_data vd = get_val2(st, reg.iplus(i));
- MATCH (vd)
- {
- CASE (const ScriptDataStr&, u)
- {
- if (u.str[0])
- c = i;
- goto continue_outer;
- }
- CASE (const ScriptDataInt&, u)
- {
- if (u.numi)
- c = i;
- goto continue_outer;
- }
- }
- abort();
- continue_outer:
- ;
- }
- return c + 1;
-}
-
-static
-void builtin_getarraysize(ScriptState *st)
-{
- SIR reg = AARGO2(2).get_if<ScriptDataVariable>()->reg;
- ZString name = variable_names.outtern(reg.base());
- char prefix = name.front();
-
- if (prefix != '$' && prefix != '@')
- {
- PRINTF("builtin_copyarray: illegal scope !\n"_fmt);
- return;
- }
-
- push_int<ScriptDataInt>(st->stack, getarraysize(st, reg));
-}
-
-/*==========================================
- * 指定要素を表す値(キー)を所得する
- *------------------------------------------
- */
-static
-void builtin_getelementofarray(ScriptState *st)
-{
- if (auto *u = AARGO2(2).get_if<ScriptDataVariable>())
- {
- int i = conv_num(st, &AARGO2(3));
- if (i > 255 || i < 0)
- {
- PRINTF("script: getelementofarray (operator[]): param2 illegal number %d\n"_fmt,
- i);
- push_int<ScriptDataInt>(st->stack, 0);
- }
- else
- {
- push_reg<ScriptDataVariable>(st->stack,
- u->reg.iplus(i));
- }
- }
- else
- {
- PRINTF("script: getelementofarray (operator[]): param1 not name !\n"_fmt);
- push_int<ScriptDataInt>(st->stack, 0);
- }
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_setlook(ScriptState *st)
-{
- LOOK type = LOOK(conv_num(st, &AARGO2(2)));
- int val = conv_num(st, &AARGO2(3));
-
- pc_changelook(script_rid2sd(st), type, val);
-
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_countitem(ScriptState *st)
-{
- ItemNameId nameid;
- int count = 0;
- dumb_ptr<map_session_data> sd;
-
- struct script_data *data;
-
- sd = script_rid2sd(st);
-
- data = &AARGO2(2);
- get_val(st, data);
- if (data->is<ScriptDataStr>())
- {
- ZString name = ZString(conv_str(st, data));
- struct item_data *item_data = itemdb_searchname(name);
- if (item_data != nullptr)
- nameid = item_data->nameid;
- }
- else
- nameid = wrap<ItemNameId>(conv_num(st, data));
-
- if (nameid)
- {
- for (IOff0 i : IOff0::iter())
- {
- if (sd->status.inventory[i].nameid == nameid)
- count += sd->status.inventory[i].amount;
- }
- }
- else
- {
- if (battle_config.error_log)
- PRINTF("wrong item ID : countitem (%i)\n"_fmt, nameid);
- }
- push_int<ScriptDataInt>(st->stack, count);
-
-}
-
-/*==========================================
- * 重量チェック
- *------------------------------------------
- */
-static
-void builtin_checkweight(ScriptState *st)
-{
- ItemNameId nameid;
- int amount;
- dumb_ptr<map_session_data> sd;
- struct script_data *data;
-
- sd = script_rid2sd(st);
-
- data = &AARGO2(2);
- get_val(st, data);
- if (data->is<ScriptDataStr>())
- {
- ZString name = ZString(conv_str(st, data));
- struct item_data *item_data = itemdb_searchname(name);
- if (item_data)
- nameid = item_data->nameid;
- }
- else
- nameid = wrap<ItemNameId>(conv_num(st, data));
-
- amount = conv_num(st, &AARGO2(3));
- if (amount <= 0 || !nameid)
- {
- //if get wrong item ID or amount<=0, don't count weight of non existing items
- push_int<ScriptDataInt>(st->stack, 0);
- return;
- }
-
- if (itemdb_weight(nameid) * amount + sd->weight > sd->max_weight)
- {
- push_int<ScriptDataInt>(st->stack, 0);
- }
- else
- {
- push_int<ScriptDataInt>(st->stack, 1);
- }
-
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_getitem(ScriptState *st)
-{
- ItemNameId nameid;
- int amount;
- dumb_ptr<map_session_data> sd;
- struct script_data *data;
-
- sd = script_rid2sd(st);
-
- data = &AARGO2(2);
- get_val(st, data);
- if (data->is<ScriptDataStr>())
- {
- ZString name = ZString(conv_str(st, data));
- struct item_data *item_data = itemdb_searchname(name);
- if (item_data != nullptr)
- nameid = item_data->nameid;
- }
- else
- nameid = wrap<ItemNameId>(conv_num(st, data));
-
- if ((amount =
- conv_num(st, &AARGO2(3))) <= 0)
- {
- return; //return if amount <=0, skip the useles iteration
- }
-
- if (nameid)
- {
- Item item_tmp {};
- item_tmp.nameid = nameid;
- if (HARGO2(5)) //アイテムを指定したIDに渡す
- sd = map_id2sd(wrap<BlockId>(conv_num(st, &AARGO2(5))));
- if (sd == nullptr) //アイテムを渡す相手がいなかったらお帰り
- return;
- PickupFail flag;
- if ((flag = pc_additem(sd, &item_tmp, amount)) != PickupFail::OKAY)
- {
- clif_additem(sd, IOff0::from(0), 0, flag);
- map_addflooritem(&item_tmp, amount,
- sd->bl_m, sd->bl_x, sd->bl_y,
- nullptr, nullptr, nullptr);
- }
- }
-
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_makeitem(ScriptState *st)
-{
- ItemNameId nameid;
- int amount;
- int x, y;
- dumb_ptr<map_session_data> sd;
- struct script_data *data;
-
- sd = script_rid2sd(st);
-
- data = &AARGO2(2);
- get_val(st, data);
- if (data->is<ScriptDataStr>())
- {
- ZString name = ZString(conv_str(st, data));
- struct item_data *item_data = itemdb_searchname(name);
- if (item_data)
- nameid = item_data->nameid;
- }
- else
- nameid = wrap<ItemNameId>(conv_num(st, data));
-
- amount = conv_num(st, &AARGO2(3));
- MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(4))));
- x = conv_num(st, &AARGO2(5));
- y = conv_num(st, &AARGO2(6));
-
- map_local *m;
- if (sd && mapname == MOB_THIS_MAP)
- m = sd->bl_m;
- else
- m = map_mapname2mapid(mapname);
-
- if (nameid)
- {
- Item item_tmp {};
- item_tmp.nameid = nameid;
-
- map_addflooritem(&item_tmp, amount, m, x, y, nullptr, nullptr, nullptr);
- }
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_delitem(ScriptState *st)
-{
- ItemNameId nameid;
- int amount;
- dumb_ptr<map_session_data> sd;
- struct script_data *data;
-
- sd = script_rid2sd(st);
-
- data = &AARGO2(2);
- get_val(st, data);
- if (data->is<ScriptDataStr>())
- {
- ZString name = ZString(conv_str(st, data));
- struct item_data *item_data = itemdb_searchname(name);
- if (item_data)
- nameid = item_data->nameid;
- }
- else
- nameid = wrap<ItemNameId>(conv_num(st, data));
-
- amount = conv_num(st, &AARGO2(3));
-
- if (!nameid || amount <= 0)
- {
- //by Lupus. Don't run FOR if u got wrong item ID or amount<=0
- return;
- }
-
- for (IOff0 i : IOff0::iter())
- {
- if (sd->status.inventory[i].nameid == nameid)
- {
- if (sd->status.inventory[i].amount >= amount)
- {
- pc_delitem(sd, i, amount, 0);
- break;
- }
- else
- {
- amount -= sd->status.inventory[i].amount;
- if (amount == 0)
- amount = sd->status.inventory[i].amount;
- pc_delitem(sd, i, amount, 0);
- break;
- }
- }
- }
-
-}
-
-/*==========================================
- *キャラ関係のパラメータ取得
- *------------------------------------------
- */
-static
-void builtin_readparam(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd;
-
- SP type = SP(conv_num(st, &AARGO2(2)));
- if (HARGO2(3))
- sd = map_nick2sd(stringish<CharName>(ZString(conv_str(st, &AARGO2(3)))));
- else
- sd = script_rid2sd(st);
-
- if (sd == nullptr)
- {
- push_int<ScriptDataInt>(st->stack, -1);
- return;
- }
-
- push_int<ScriptDataInt>(st->stack, pc_readparam(sd, type));
-
-}
-
-/*==========================================
- *キャラ関係のID取得
- *------------------------------------------
- */
-static
-void builtin_getcharid(ScriptState *st)
-{
- int num;
- dumb_ptr<map_session_data> sd;
-
- num = conv_num(st, &AARGO2(2));
- if (HARGO2(3))
- sd = map_nick2sd(stringish<CharName>(ZString(conv_str(st, &AARGO2(3)))));
- else
- sd = script_rid2sd(st);
- if (sd == nullptr)
- {
- push_int<ScriptDataInt>(st->stack, -1);
- return;
- }
- if (num == 0)
- push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status_key.char_id));
- if (num == 1)
- push_int<ScriptDataInt>(st->stack, unwrap<PartyId>(sd->status.party_id));
- if (num == 2)
- push_int<ScriptDataInt>(st->stack, 0/*guild_id*/);
- if (num == 3)
- push_int<ScriptDataInt>(st->stack, unwrap<AccountId>(sd->status_key.account_id));
-}
-
-/*==========================================
- *指定IDのPT名取得
- *------------------------------------------
- */
-static
-RString builtin_getpartyname_sub(PartyId party_id)
-{
- PartyPair p = party_search(party_id);
-
- if (p)
- return p->name;
-
- return RString();
-}
-
-/*==========================================
- * キャラクタの名前
- *------------------------------------------
- */
-static
-void builtin_strcharinfo(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd;
- int num;
-
- sd = script_rid2sd(st);
- num = conv_num(st, &AARGO2(2));
- if (num == 0)
- {
- RString buf = sd->status_key.name.to__actual();
- push_str<ScriptDataStr>(st->stack, buf);
- }
- if (num == 1)
- {
- RString buf = builtin_getpartyname_sub(sd->status.party_id);
- if (buf)
- push_str<ScriptDataStr>(st->stack, buf);
- else
- push_str<ScriptDataStr>(st->stack, ""_s);
- }
- if (num == 2)
- {
- // was: guild name
- push_str<ScriptDataStr>(st->stack, ""_s);
- }
-
-}
-
-// indexed by the equip_* in db/const.txt
-// TODO change to use EQUIP
-static
-Array<EPOS, 11> equip //=
-{{
- EPOS::HAT,
- EPOS::MISC1,
- EPOS::SHIELD,
- EPOS::WEAPON,
- EPOS::GLOVES,
- EPOS::SHOES,
- EPOS::CAPE,
- EPOS::MISC2,
- EPOS::TORSO,
- EPOS::LEGS,
- EPOS::ARROW,
-}};
-
-/*==========================================
- * GetEquipID(Pos); Pos: 1-10
- *------------------------------------------
- */
-static
-void builtin_getequipid(ScriptState *st)
-{
- int num;
- dumb_ptr<map_session_data> sd;
- struct item_data *item;
-
- sd = script_rid2sd(st);
- if (sd == nullptr)
- {
- PRINTF("getequipid: sd == nullptr\n"_fmt);
- return;
- }
- num = conv_num(st, &AARGO2(2));
- IOff0 i = pc_checkequip(sd, equip[num - 1]);
- if (i.ok())
- {
- item = sd->inventory_data[i];
- if (item)
- push_int<ScriptDataInt>(st->stack, unwrap<ItemNameId>(item->nameid));
- else
- push_int<ScriptDataInt>(st->stack, 0);
- }
- else
- {
- push_int<ScriptDataInt>(st->stack, -1);
- }
-}
-
-/*==========================================
- * 装備名文字列(精錬メニュー用)
- *------------------------------------------
- */
-static
-void builtin_getequipname(ScriptState *st)
-{
- int num;
- dumb_ptr<map_session_data> sd;
- struct item_data *item;
-
- AString buf;
-
- sd = script_rid2sd(st);
- num = conv_num(st, &AARGO2(2));
- IOff0 i = pc_checkequip(sd, equip[num - 1]);
- if (i.ok())
- {
- item = sd->inventory_data[i];
- if (item)
- buf = STRPRINTF("%s-[%s]"_fmt, pos_str[num - 1], item->jname);
- else
- buf = STRPRINTF("%s-[%s]"_fmt, pos_str[num - 1], pos_str[10]);
- }
- else
- {
- buf = STRPRINTF("%s-[%s]"_fmt, pos_str[num - 1], pos_str[10]);
- }
- push_str<ScriptDataStr>(st->stack, buf);
-
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_statusup2(ScriptState *st)
-{
- SP type = SP(conv_num(st, &AARGO2(2)));
- int val = conv_num(st, &AARGO2(3));
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- pc_statusup2(sd, type, val);
-
-}
-
-/*==========================================
- * 装備品による能力値ボーナス
- *------------------------------------------
- */
-static
-void builtin_bonus(ScriptState *st)
-{
- SP type = SP(conv_num(st, &AARGO2(2)));
- int val = conv_num(st, &AARGO2(3));
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- pc_bonus(sd, type, val);
-
-}
-
-/*==========================================
- * 装備品による能力値ボーナス
- *------------------------------------------
- */
-static
-void builtin_bonus2(ScriptState *st)
-{
- SP type = SP(conv_num(st, &AARGO2(2)));
- int type2 = conv_num(st, &AARGO2(3));
- int val = conv_num(st, &AARGO2(4));
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- pc_bonus2(sd, type, type2, val);
-
-}
-
-/*==========================================
- * スキル所得
- *------------------------------------------
- */
-static
-void builtin_skill(ScriptState *st)
-{
- int level, flag = 1;
- dumb_ptr<map_session_data> sd;
-
- SkillID id = SkillID(conv_num(st, &AARGO2(2)));
- level = conv_num(st, &AARGO2(3));
- if (HARGO2(4))
- flag = conv_num(st, &AARGO2(4));
- sd = script_rid2sd(st);
- pc_skill(sd, id, level, flag);
- clif_skillinfoblock(sd);
-
-}
-
-/*==========================================
- * [Fate] Sets the skill level permanently
- *------------------------------------------
- */
-static
-void builtin_setskill(ScriptState *st)
-{
- int level;
- dumb_ptr<map_session_data> sd;
-
- SkillID id = static_cast<SkillID>(conv_num(st, &AARGO2(2)));
- level = conv_num(st, &AARGO2(3));
- sd = script_rid2sd(st);
-
- level = std::min(level, MAX_SKILL_LEVEL);
- level = std::max(level, 0);
- sd->status.skill[id].lv = level;
- clif_skillinfoblock(sd);
-}
-
-/*==========================================
- * スキルレベル所得
- *------------------------------------------
- */
-static
-void builtin_getskilllv(ScriptState *st)
-{
- SkillID id = SkillID(conv_num(st, &AARGO2(2)));
- push_int<ScriptDataInt>(st->stack, pc_checkskill(script_rid2sd(st), id));
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_getgmlevel(ScriptState *st)
-{
- push_int<ScriptDataInt>(st->stack, pc_isGM(script_rid2sd(st)).get_all_bits());
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_end(ScriptState *st)
-{
- st->state = ScriptEndState::END;
-}
-
-/*==========================================
- * [Freeyorp] Return the current opt2
- *------------------------------------------
- */
-
-static
-void builtin_getopt2(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd;
-
- sd = script_rid2sd(st);
-
- push_int<ScriptDataInt>(st->stack, static_cast<uint16_t>(sd->opt2));
-
-}
-
-/*==========================================
- * [Freeyorp] Sets opt2
- *------------------------------------------
- */
-
-static
-void builtin_setopt2(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd;
-
- Opt2 new_opt2 = Opt2(conv_num(st, &AARGO2(2)));
- sd = script_rid2sd(st);
- if (new_opt2 == sd->opt2)
- return;
- sd->opt2 = new_opt2;
- clif_changeoption(sd);
- pc_calcstatus(sd, 0);
-
-}
-
-/*==========================================
- * セーブポイントの保存
- *------------------------------------------
- */
-static
-void builtin_savepoint(ScriptState *st)
-{
- int x, y;
-
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- x = conv_num(st, &AARGO2(3));
- y = conv_num(st, &AARGO2(4));
- pc_setsavepoint(script_rid2sd(st), str, x, y);
-}
-
-/*==========================================
- * gettimetick(type)
- *
- * type The type of time measurement.
- * Specify 0 for the system tick, 1 for
- * seconds elapsed today, or 2 for seconds
- * since Unix epoch. Defaults to 0 for any
- * other value.
- *------------------------------------------
- */
-static
-void builtin_gettimetick(ScriptState *st) /* Asgard Version */
-{
- int type;
- type = conv_num(st, &AARGO2(2));
-
- switch (type)
- {
- /* Number of seconds elapsed today(0-86399, 00:00:00-23:59:59). */
- case 1:
- {
- struct tm t = TimeT::now();
- push_int<ScriptDataInt>(st->stack,
- t.tm_hour * 3600 + t.tm_min * 60 + t.tm_sec);
- break;
- }
- /* Seconds since Unix epoch. */
- case 2:
- push_int<ScriptDataInt>(st->stack, static_cast<time_t>(TimeT::now()));
- break;
- /* System tick(unsigned int, and yes, it will wrap). */
- case 0:
- default:
- push_int<ScriptDataInt>(st->stack, gettick().time_since_epoch().count());
- break;
- }
-}
-
-/*==========================================
- * GetTime(Type);
- * 1: Sec 2: Min 3: Hour
- * 4: WeekDay 5: MonthDay 6: Month
- * 7: Year
- *------------------------------------------
- */
-static
-void builtin_gettime(ScriptState *st) /* Asgard Version */
-{
- int type = conv_num(st, &AARGO2(2));
-
- struct tm t = TimeT::now();
-
- switch (type)
- {
- case 1: //Sec(0~59)
- push_int<ScriptDataInt>(st->stack, t.tm_sec);
- break;
- case 2: //Min(0~59)
- push_int<ScriptDataInt>(st->stack, t.tm_min);
- break;
- case 3: //Hour(0~23)
- push_int<ScriptDataInt>(st->stack, t.tm_hour);
- break;
- case 4: //WeekDay(0~6)
- push_int<ScriptDataInt>(st->stack, t.tm_wday);
- break;
- case 5: //MonthDay(01~31)
- push_int<ScriptDataInt>(st->stack, t.tm_mday);
- break;
- case 6: //Month(01~12)
- push_int<ScriptDataInt>(st->stack, t.tm_mon + 1);
- break;
- case 7: //Year(20xx)
- push_int<ScriptDataInt>(st->stack, t.tm_year + 1900);
- break;
- default: //(format error)
- push_int<ScriptDataInt>(st->stack, -1);
- break;
- }
-}
-
-/*==========================================
- * カプラ倉庫を開く
- *------------------------------------------
- */
-static
-void builtin_openstorage(ScriptState *st)
-{
-// int sync = 0;
-// if (st->end >= 3) sync = conv_num(st,& (st->stack->stack_data[st->start+2]));
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
-// if (sync) {
- st->state = ScriptEndState::STOP;
- sd->npc_flags.storage = 1;
-// } else st->state = ScriptEndState::END;
-
- storage_storageopen(sd);
-}
-
-/*==========================================
- * NPCで経験値上げる
- *------------------------------------------
- */
-static
-void builtin_getexp(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- int base = 0, job = 0;
-
- base = conv_num(st, &AARGO2(2));
- job = conv_num(st, &AARGO2(3));
- if (base < 0 || job < 0)
- return;
- if (sd)
- pc_gainexp_reason(sd, base, job, PC_GAINEXP_REASON::SCRIPT);
-
-}
-
-/*==========================================
- * モンスター発生
- *------------------------------------------
- */
-static
-void builtin_monster(ScriptState *st)
-{
- Species mob_class;
- int amount, x, y;
- NpcEvent event;
-
- MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- x = conv_num(st, &AARGO2(3));
- y = conv_num(st, &AARGO2(4));
- MobName str = stringish<MobName>(ZString(conv_str(st, &AARGO2(5))));
- mob_class = wrap<Species>(conv_num(st, &AARGO2(6)));
- amount = conv_num(st, &AARGO2(7));
- if (HARGO2(8))
- extract(ZString(conv_str(st, &AARGO2(8))), &event);
-
- mob_once_spawn(map_id2sd(st->rid), mapname, x, y, str, mob_class, amount,
- event);
-}
-
-/*==========================================
- * モンスター発生
- *------------------------------------------
- */
-static
-void builtin_areamonster(ScriptState *st)
-{
- Species mob_class;
- int amount, x0, y0, x1, y1;
- NpcEvent event;
-
- MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- x0 = conv_num(st, &AARGO2(3));
- y0 = conv_num(st, &AARGO2(4));
- x1 = conv_num(st, &AARGO2(5));
- y1 = conv_num(st, &AARGO2(6));
- MobName str = stringish<MobName>(ZString(conv_str(st, &AARGO2(7))));
- mob_class = wrap<Species>(conv_num(st, &AARGO2(8)));
- amount = conv_num(st, &AARGO2(9));
- if (HARGO2(10))
- extract(ZString(conv_str(st, &AARGO2(10))), &event);
-
- mob_once_spawn_area(map_id2sd(st->rid), mapname, x0, y0, x1, y1, str, mob_class,
- amount, event);
-}
-
-/*==========================================
- * モンスター削除
- *------------------------------------------
- */
-static
-void builtin_killmonster_sub(dumb_ptr<block_list> bl, NpcEvent event)
-{
- dumb_ptr<mob_data> md = bl->is_mob();
- if (event)
- {
- if (event == md->npc_event)
- mob_delete(md);
- return;
- }
- else if (!event)
- {
- if (md->spawn.delay1 == static_cast<interval_t>(-1)
- && md->spawn.delay2 == static_cast<interval_t>(-1))
- mob_delete(md);
- return;
- }
-}
-
-static
-void builtin_killmonster(ScriptState *st)
-{
- MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- ZString event_ = ZString(conv_str(st, &AARGO2(3)));
- NpcEvent event;
- if (event_ != "All"_s)
- extract(event_, &event);
-
- map_local *m = map_mapname2mapid(mapname);
- if (m == nullptr)
- return;
- map_foreachinarea(std::bind(builtin_killmonster_sub, ph::_1, event),
- m,
- 0, 0,
- m->xs, m->ys,
- BL::MOB);
-}
-
-static
-void builtin_killmonsterall_sub(dumb_ptr<block_list> bl)
-{
- mob_delete(bl->is_mob());
-}
-
-static
-void builtin_killmonsterall(ScriptState *st)
-{
- MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
-
- map_local *m = map_mapname2mapid(mapname);
- if (m == nullptr)
- return;
- map_foreachinarea(builtin_killmonsterall_sub,
- m,
- 0, 0,
- m->xs, m->ys,
- BL::MOB);
-}
-
-/*==========================================
- * NPC主体イベント実行
- *------------------------------------------
- */
-static
-void builtin_donpcevent(ScriptState *st)
-{
- ZString event_ = ZString(conv_str(st, &AARGO2(2)));
- NpcEvent event;
- extract(event_, &event);
- npc_event_do(event);
-}
-
-/*==========================================
- * イベントタイマー追加
- *------------------------------------------
- */
-static
-void builtin_addtimer(ScriptState *st)
-{
- interval_t tick = static_cast<interval_t>(conv_num(st, &AARGO2(2)));
- ZString event_ = ZString(conv_str(st, &AARGO2(3)));
- NpcEvent event;
- extract(event_, &event);
- pc_addeventtimer(script_rid2sd(st), tick, event);
-}
-
-/*==========================================
- * NPCタイマー初期化
- *------------------------------------------
- */
-static
-void builtin_initnpctimer(ScriptState *st)
-{
- dumb_ptr<npc_data> nd_;
- if (HARGO2(2))
- nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARGO2(2)))));
- else
- nd_ = map_id_is_npc(st->oid);
- assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
- dumb_ptr<npc_data_script> nd = nd_->is_script();
-
- npc_settimerevent_tick(nd, interval_t::zero());
- npc_timerevent_start(nd);
-}
-
-/*==========================================
- * NPCタイマー開始
- *------------------------------------------
- */
-static
-void builtin_startnpctimer(ScriptState *st)
-{
- dumb_ptr<npc_data> nd_;
- if (HARGO2(2))
- nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARGO2(2)))));
- else
- nd_ = map_id_is_npc(st->oid);
- assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
- dumb_ptr<npc_data_script> nd = nd_->is_script();
-
- npc_timerevent_start(nd);
-}
-
-/*==========================================
- * NPCタイマー停止
- *------------------------------------------
- */
-static
-void builtin_stopnpctimer(ScriptState *st)
-{
- dumb_ptr<npc_data> nd_;
- if (HARGO2(2))
- nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARGO2(2)))));
- else
- nd_ = map_id_is_npc(st->oid);
- assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
- dumb_ptr<npc_data_script> nd = nd_->is_script();
-
- npc_timerevent_stop(nd);
-}
-
-/*==========================================
- * NPCタイマー情報所得
- *------------------------------------------
- */
-static
-void builtin_getnpctimer(ScriptState *st)
-{
- dumb_ptr<npc_data> nd_;
- int type = conv_num(st, &AARGO2(2));
- int val = 0;
- if (HARGO2(3))
- nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARGO2(3)))));
- else
- nd_ = map_id_is_npc(st->oid);
- assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
- dumb_ptr<npc_data_script> nd = nd_->is_script();
-
- switch (type)
- {
- case 0:
- val = npc_gettimerevent_tick(nd).count();
- break;
- case 1:
- val = nd->scr.timer_active;
- break;
- case 2:
- val = nd->scr.timer_eventv.size();
- break;
- }
- push_int<ScriptDataInt>(st->stack, val);
-}
-
-/*==========================================
- * NPCタイマー値設定
- *------------------------------------------
- */
-static
-void builtin_setnpctimer(ScriptState *st)
-{
- dumb_ptr<npc_data> nd_;
- interval_t tick = static_cast<interval_t>(conv_num(st, &AARGO2(2)));
- if (HARGO2(3))
- nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARGO2(3)))));
- else
- nd_ = map_id_is_npc(st->oid);
- assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
- dumb_ptr<npc_data_script> nd = nd_->is_script();
-
- npc_settimerevent_tick(nd, tick);
-}
-
-/*==========================================
- * 天の声アナウンス
- *------------------------------------------
- */
-static
-void builtin_announce(ScriptState *st)
-{
- int flag;
- ZString str = ZString(conv_str(st, &AARGO2(2)));
- flag = conv_num(st, &AARGO2(3));
-
- if (flag & 0x0f)
- {
- dumb_ptr<block_list> bl;
- if (flag & 0x08)
- bl = map_id2bl(st->oid);
- else
- bl = script_rid2sd(st);
- clif_GMmessage(bl, str, flag);
- }
- else
- intif_GMmessage(str);
-}
-
-/*==========================================
- * 天の声アナウンス(特定マップ)
- *------------------------------------------
- */
-static
-void builtin_mapannounce_sub(dumb_ptr<block_list> bl, XString str, int flag)
-{
- clif_GMmessage(bl, str, flag | 3);
-}
-
-static
-void builtin_mapannounce(ScriptState *st)
-{
- int flag;
-
- MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- ZString str = ZString(conv_str(st, &AARGO2(3)));
- flag = conv_num(st, &AARGO2(4));
-
- map_local *m = map_mapname2mapid(mapname);
- if (m == nullptr)
- return;
- map_foreachinarea(std::bind(builtin_mapannounce_sub, ph::_1, str, flag & 0x10),
- m,
- 0, 0,
- m->xs, m->ys,
- BL::PC);
-}
-
-/*==========================================
- * ユーザー数所得
- *------------------------------------------
- */
-static
-void builtin_getusers(ScriptState *st)
-{
- int flag = conv_num(st, &AARGO2(2));
- dumb_ptr<block_list> bl = map_id2bl((flag & 0x08) ? st->oid : st->rid);
- int val = 0;
- switch (flag & 0x07)
- {
- case 0:
- val = bl->bl_m->users;
- break;
- case 1:
- val = map_getusers();
- break;
- }
- push_int<ScriptDataInt>(st->stack, val);
-}
-
-/*==========================================
- * マップ指定ユーザー数所得
- *------------------------------------------
- */
-static
-void builtin_getmapusers(ScriptState *st)
-{
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- map_local *m = map_mapname2mapid(str);
- if (m == nullptr)
- {
- push_int<ScriptDataInt>(st->stack, -1);
- return;
- }
- push_int<ScriptDataInt>(st->stack, m->users);
-}
-
-/*==========================================
- * エリア指定ユーザー数所得
- *------------------------------------------
- */
-static
-void builtin_getareausers_sub(dumb_ptr<block_list> bl, int *users)
-{
- if (bool(bl->is_player()->status.option & Option::HIDE))
- return;
- (*users)++;
-}
-
-static
-void builtin_getareausers_living_sub(dumb_ptr<block_list> bl, int *users)
-{
- if (bool(bl->is_player()->status.option & Option::HIDE))
- return;
- if (!pc_isdead(bl->is_player()))
- (*users)++;
-}
-
-static
-void builtin_getareausers(ScriptState *st)
-{
- int x0, y0, x1, y1, users = 0;
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- x0 = conv_num(st, &AARGO2(3));
- y0 = conv_num(st, &AARGO2(4));
- x1 = conv_num(st, &AARGO2(5));
- y1 = conv_num(st, &AARGO2(6));
-
- int living = 0;
- if (HARGO2(7))
- {
- living = conv_num(st, &AARGO2(7));
- }
- map_local *m = map_mapname2mapid(str);
- if (m == nullptr)
- {
- push_int<ScriptDataInt>(st->stack, -1);
- return;
- }
- map_foreachinarea(std::bind(living ? builtin_getareausers_living_sub: builtin_getareausers_sub, ph::_1, &users),
- m,
- x0, y0,
- x1, y1,
- BL::PC);
- push_int<ScriptDataInt>(st->stack, users);
-}
-
-/*==========================================
- * エリア指定ドロップアイテム数所得
- *------------------------------------------
- */
-static
-void builtin_getareadropitem_sub(dumb_ptr<block_list> bl, ItemNameId item, int *amount)
-{
- dumb_ptr<flooritem_data> drop = bl->is_item();
-
- if (drop->item_data.nameid == item)
- (*amount) += drop->item_data.amount;
-
-}
-
-static
-void builtin_getareadropitem_sub_anddelete(dumb_ptr<block_list> bl, ItemNameId item, int *amount)
-{
- dumb_ptr<flooritem_data> drop = bl->is_item();
-
- if (drop->item_data.nameid == item)
- {
- (*amount) += drop->item_data.amount;
- clif_clearflooritem(drop, nullptr);
- map_delobject(drop->bl_id, drop->bl_type);
- }
-}
-
-static
-void builtin_getareadropitem(ScriptState *st)
-{
- ItemNameId item;
- int x0, y0, x1, y1, amount = 0, delitems = 0;
- struct script_data *data;
-
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- x0 = conv_num(st, &AARGO2(3));
- y0 = conv_num(st, &AARGO2(4));
- x1 = conv_num(st, &AARGO2(5));
- y1 = conv_num(st, &AARGO2(6));
-
- data = &AARGO2(7);
- get_val(st, data);
- if (data->is<ScriptDataStr>())
- {
- ZString name = ZString(conv_str(st, data));
- struct item_data *item_data = itemdb_searchname(name);
- if (item_data)
- item = item_data->nameid;
- }
- else
- item = wrap<ItemNameId>(conv_num(st, data));
-
- if (HARGO2(8))
- delitems = conv_num(st, &AARGO2(8));
-
- map_local *m = map_mapname2mapid(str);
- if (m == nullptr)
- {
- push_int<ScriptDataInt>(st->stack, -1);
- return;
- }
- if (delitems)
- map_foreachinarea(std::bind(builtin_getareadropitem_sub_anddelete, ph::_1, item, &amount),
- m,
- x0, y0,
- x1, y1,
- BL::ITEM);
- else
- map_foreachinarea(std::bind(builtin_getareadropitem_sub, ph::_1, item, &amount),
- m,
- x0, y0,
- x1, y1,
- BL::ITEM);
-
- push_int<ScriptDataInt>(st->stack, amount);
-}
-
-/*==========================================
- * NPCの有効化
- *------------------------------------------
- */
-static
-void builtin_enablenpc(ScriptState *st)
-{
- NpcName str = stringish<NpcName>(ZString(conv_str(st, &AARGO2(2))));
- npc_enable(str, 1);
-}
-
-/*==========================================
- * NPCの無効化
- *------------------------------------------
- */
-static
-void builtin_disablenpc(ScriptState *st)
-{
- NpcName str = stringish<NpcName>(ZString(conv_str(st, &AARGO2(2))));
- npc_enable(str, 0);
-}
-
-/*==========================================
- * 状態異常にかかる
- *------------------------------------------
- */
-static
-void builtin_sc_start(ScriptState *st)
-{
- dumb_ptr<block_list> bl;
- int val1;
- StatusChange type = static_cast<StatusChange>(conv_num(st, &AARGO2(2)));
- interval_t tick = static_cast<interval_t>(conv_num(st, &AARGO2(3)));
- if (tick < 1_s)
- // work around old behaviour of:
- // speed potion
- // atk potion
- // matk potion
- //
- // which used to use seconds
- // all others used milliseconds
- tick *= 1000;
- val1 = conv_num(st, &AARGO2(4));
- if (HARGO2(5)) //指定したキャラを状態異常にする
- bl = map_id2bl(wrap<BlockId>(conv_num(st, &AARGO2(5))));
- else
- bl = map_id2bl(st->rid);
- skill_status_change_start(bl, type, val1, tick);
-}
-
-/*==========================================
- * 状態異常が直る
- *------------------------------------------
- */
-static
-void builtin_sc_end(ScriptState *st)
-{
- dumb_ptr<block_list> bl;
- StatusChange type = StatusChange(conv_num(st, &AARGO2(2)));
- bl = map_id2bl(st->rid);
- skill_status_change_end(bl, type, nullptr);
-}
-
-static
-void builtin_sc_check(ScriptState *st)
-{
- dumb_ptr<block_list> bl;
- StatusChange type = StatusChange(conv_num(st, &AARGO2(2)));
- bl = map_id2bl(st->rid);
-
- push_int<ScriptDataInt>(st->stack, skill_status_change_active(bl, type));
-
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_debugmes(ScriptState *st)
-{
- RString mes = conv_str(st, &AARGO2(2));
- PRINTF("script debug : %d %d : %s\n"_fmt,
- st->rid, st->oid, mes);
-}
-
-/*==========================================
- * ステータスリセット
- *------------------------------------------
- */
-static
-void builtin_resetstatus(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd;
- sd = script_rid2sd(st);
- pc_resetstate(sd);
-}
-
-/*==========================================
- * 性別変換
- *------------------------------------------
- */
-static
-void builtin_changesex(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = nullptr;
- sd = script_rid2sd(st);
-
- chrif_char_ask_name(AccountId(), sd->status_key.name, 5, HumanTimeDiff()); // type: 5 - changesex
- chrif_save(sd);
-}
-
-/*==========================================
- * RIDのアタッチ
- *------------------------------------------
- */
-static
-void builtin_attachrid(ScriptState *st)
-{
- st->rid = wrap<BlockId>(conv_num(st, &AARGO2(2)));
- push_int<ScriptDataInt>(st->stack, (map_id2sd(st->rid) != nullptr));
-}
-
-/*==========================================
- * RIDのデタッチ
- *------------------------------------------
- */
-static
-void builtin_detachrid(ScriptState *st)
-{
- st->rid = BlockId();
-}
-
-/*==========================================
- * 存在チェック
- *------------------------------------------
- */
-static
-void builtin_isloggedin(ScriptState *st)
-{
- push_int<ScriptDataInt>(st->stack,
- map_id2sd(wrap<BlockId>(conv_num(st, &AARGO2(2)))) != nullptr);
-}
-
-static
-void builtin_setmapflag(ScriptState *st)
-{
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- int i = conv_num(st, &AARGO2(3));
- MapFlag mf = map_flag_from_int(i);
- map_local *m = map_mapname2mapid(str);
- if (m != nullptr)
- {
- m->flag.set(mf, 1);
- }
-}
-
-static
-void builtin_removemapflag(ScriptState *st)
-{
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- int i = conv_num(st, &AARGO2(3));
- MapFlag mf = map_flag_from_int(i);
- map_local *m = map_mapname2mapid(str);
- if (m != nullptr)
- {
- m->flag.set(mf, 0);
- }
-}
-
-static
-void builtin_getmapflag(ScriptState *st)
-{
- int r = -1;
-
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- int i = conv_num(st, &AARGO2(3));
- MapFlag mf = map_flag_from_int(i);
- map_local *m = map_mapname2mapid(str);
- if (m != nullptr)
- {
- r = m->flag.get(mf);
- }
-
- push_int<ScriptDataInt>(st->stack, r);
-}
-
-static
-void builtin_pvpon(ScriptState *st)
-{
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- map_local *m = map_mapname2mapid(str);
- if (m != nullptr && !m->flag.get(MapFlag::PVP) && !m->flag.get(MapFlag::NOPVP))
- {
- m->flag.set(MapFlag::PVP, 1);
-
- if (battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris]
- return;
-
- for (io::FD i : iter_fds())
- {
- Session *s = get_session(i);
- if (!s)
- continue;
- map_session_data *pl_sd = static_cast<map_session_data *>(s->session_data.get());
- if (pl_sd && pl_sd->state.auth)
- {
- if (m == pl_sd->bl_m && !pl_sd->pvp_timer)
- {
- pl_sd->pvp_timer = Timer(gettick() + 200_ms,
- std::bind(pc_calc_pvprank_timer, ph::_1, ph::_2,
- pl_sd->bl_id));
- pl_sd->pvp_rank = 0;
- pl_sd->pvp_lastusers = 0;
- pl_sd->pvp_point = 5;
- }
- }
- }
- }
-
-}
-
-static
-void builtin_pvpoff(ScriptState *st)
-{
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- map_local *m = map_mapname2mapid(str);
- if (m != nullptr && m->flag.get(MapFlag::PVP) && m->flag.get(MapFlag::NOPVP))
- {
- m->flag.set(MapFlag::PVP, 0);
-
- if (battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris]
- return;
-
- for (io::FD i : iter_fds())
- {
- Session *s = get_session(i);
- if (!s)
- continue;
- map_session_data *pl_sd = static_cast<map_session_data *>(s->session_data.get());
- if (pl_sd && pl_sd->state.auth)
- {
- if (m == pl_sd->bl_m)
- {
- pl_sd->pvp_timer.cancel();
- }
- }
- }
- }
-
-}
-
-/*==========================================
- * NPCエモーション
- *------------------------------------------
- */
-
-static
-void builtin_emotion(ScriptState *st)
-{
- int type;
- type = conv_num(st, &AARGO2(2));
- if (type < 0 || type > 100)
- return;
- clif_emotion(map_id2bl(st->oid), type);
-}
-
-static
-void builtin_mapwarp(ScriptState *st) // Added by RoVeRT
-{
- int x, y;
- int x0, y0, x1, y1;
-
- MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- x0 = 0;
- y0 = 0;
- map_local *m = map_mapname2mapid(mapname);
- x1 = m->xs;
- y1 = m->ys;
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(3))));
- x = conv_num(st, &AARGO2(4));
- y = conv_num(st, &AARGO2(5));
-
- if (m == nullptr)
- return;
-
- map_foreachinarea(std::bind(builtin_areawarp_sub, ph::_1, str, x, y),
- m,
- x0, y0,
- x1, y1,
- BL::PC);
-}
-
-static
-void builtin_cmdothernpc(ScriptState *st) // Added by RoVeRT
-{
- NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARGO2(2))));
- ZString command = ZString(conv_str(st, &AARGO2(3)));
-
- npc_command(map_id2sd(st->rid), npc, command);
-}
-
-static
-void builtin_mobcount_sub(dumb_ptr<block_list> bl, NpcEvent event, int *c)
-{
- if (event == bl->is_mob()->npc_event)
- (*c)++;
-}
-
-static
-void builtin_mobcount(ScriptState *st) // Added by RoVeRT
-{
- int c = 0;
- MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- ZString event_ = ZString(conv_str(st, &AARGO2(3)));
- NpcEvent event;
- extract(event_, &event);
-
- map_local *m = map_mapname2mapid(mapname);
- if (m == nullptr)
- {
- push_int<ScriptDataInt>(st->stack, -1);
- return;
- }
- map_foreachinarea(std::bind(builtin_mobcount_sub, ph::_1, event, &c),
- m,
- 0, 0,
- m->xs, m->ys,
- BL::MOB);
-
- push_int<ScriptDataInt>(st->stack, (c - 1));
-
-}
-
-static
-void builtin_marriage(ScriptState *st)
-{
- CharName partner = stringish<CharName>(ZString(conv_str(st, &AARGO2(2))));
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- dumb_ptr<map_session_data> p_sd = map_nick2sd(partner);
-
- if (sd == nullptr || p_sd == nullptr || pc_marriage(sd, p_sd) < 0)
- {
- push_int<ScriptDataInt>(st->stack, 0);
- return;
- }
- push_int<ScriptDataInt>(st->stack, 1);
-}
-
-static
-void builtin_divorce(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- st->state = ScriptEndState::STOP; // rely on pc_divorce to restart
-
- sd->npc_flags.divorce = 1;
-
- if (sd == nullptr || pc_divorce(sd) < 0)
- {
- push_int<ScriptDataInt>(st->stack, 0);
- return;
- }
-
- push_int<ScriptDataInt>(st->stack, 1);
-}
-
-/*==========================================
- * IDからItem名
- *------------------------------------------
- */
-static
-void builtin_getitemname(ScriptState *st)
-{
- struct item_data *i_data;
- struct script_data *data;
-
- data = &AARGO2(2);
- get_val(st, data);
- if (data->is<ScriptDataStr>())
- {
- ZString name = ZString(conv_str(st, data));
- i_data = itemdb_searchname(name);
- }
- else
- {
- ItemNameId item_id = wrap<ItemNameId>(conv_num(st, data));
- i_data = itemdb_search(item_id);
- }
-
- RString item_name;
- if (i_data)
- item_name = i_data->jname;
- else
- item_name = "Unknown Item"_s;
-
- push_str<ScriptDataStr>(st->stack, item_name);
-}
-
-static
-void builtin_getspellinvocation(ScriptState *st)
-{
- RString name = conv_str(st, &AARGO2(2));
-
- AString invocation = magic::magic_find_invocation(name);
- if (!invocation)
- invocation = "..."_s;
-
- push_str<ScriptDataStr>(st->stack, invocation);
-}
-
-static
-void builtin_getpartnerid2(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status.partner_id));
-}
-
-/*==========================================
- * PCの所持品情報読み取り
- *------------------------------------------
- */
-static
-void builtin_getinventorylist(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- int j = 0;
- if (!sd)
- return;
- for (IOff0 i : IOff0::iter())
- {
- if (sd->status.inventory[i].nameid
- && sd->status.inventory[i].amount > 0)
- {
- pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_id"_s), j),
- unwrap<ItemNameId>(sd->status.inventory[i].nameid));
- pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_amount"_s), j),
- sd->status.inventory[i].amount);
- pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_equip"_s), j),
- static_cast<uint16_t>(sd->status.inventory[i].equip));
- j++;
- }
- }
- pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_count"_s)), j);
-}
-
-static
-void builtin_getactivatedpoolskilllist(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- SkillID pool_skills[MAX_SKILL_POOL];
- int skill_pool_size = skill_pool(sd, pool_skills);
- int i, count = 0;
-
- if (!sd)
- return;
-
- for (i = 0; i < skill_pool_size; i++)
- {
- SkillID skill_id = pool_skills[i];
-
- if (sd->status.skill[skill_id].lv)
- {
- pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_id"_s), count),
- static_cast<uint16_t>(skill_id));
- pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_lv"_s), count),
- sd->status.skill[skill_id].lv);
- pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_flag"_s), count),
- static_cast<uint16_t>(sd->status.skill[skill_id].flags));
- pc_setregstr(sd, SIR::from(variable_names.intern("@skilllist_name$"_s), count),
- skill_name(skill_id));
- ++count;
- }
- }
- pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_count"_s)), count);
-
-}
-
-static
-void builtin_getunactivatedpoolskilllist(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- int i, count = 0;
-
- if (!sd)
- return;
-
- for (i = 0; i < skill_pool_skills_size; i++)
- {
- SkillID skill_id = skill_pool_skills[i];
-
- if (sd->status.skill[skill_id].lv
- && !bool(sd->status.skill[skill_id].flags & SkillFlags::POOL_ACTIVATED))
- {
- pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_id"_s), count),
- static_cast<uint16_t>(skill_id));
- pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_lv"_s), count),
- sd->status.skill[skill_id].lv);
- pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_flag"_s), count),
- static_cast<uint16_t>(sd->status.skill[skill_id].flags));
- pc_setregstr(sd, SIR::from(variable_names.intern("@skilllist_name$"_s), count),
- skill_name(skill_id));
- ++count;
- }
- }
- pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_count"_s)), count);
-}
-
-static
-void builtin_poolskill(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- SkillID skill_id = SkillID(conv_num(st, &AARGO2(2)));
-
- skill_pool_activate(sd, skill_id);
- clif_skillinfoblock(sd);
-
-}
-
-static
-void builtin_unpoolskill(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- SkillID skill_id = SkillID(conv_num(st, &AARGO2(2)));
-
- skill_pool_deactivate(sd, skill_id);
- clif_skillinfoblock(sd);
-
-}
-
-/*==========================================
- * NPCから発生するエフェクト
- * misceffect(effect, [target])
- *
- * effect The effect type/ID.
- * target The player name or being ID on
- * which to display the effect. If not
- * specified, it attempts to default to
- * the current NPC or invoking PC.
- *------------------------------------------
- */
-static
-void builtin_misceffect(ScriptState *st)
-{
- int type;
- BlockId id;
- CharName name;
- dumb_ptr<block_list> bl = nullptr;
-
- type = conv_num(st, &AARGO2(2));
-
- if (HARGO2(3))
- {
- struct script_data *sdata = &AARGO2(3);
-
- get_val(st, sdata);
-
- if (sdata->is<ScriptDataStr>())
- name = stringish<CharName>(ZString(conv_str(st, sdata)));
- else
- id = wrap<BlockId>(conv_num(st, sdata));
- }
-
- if (name.to__actual())
- {
- dumb_ptr<map_session_data> sd = map_nick2sd(name);
- if (sd)
- bl = sd;
- }
- else if (id)
- bl = map_id2bl(id);
- else if (st->oid)
- bl = map_id2bl(st->oid);
- else
- {
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- if (sd)
- bl = sd;
- }
-
- if (bl)
- clif_misceffect(bl, type);
-
-}
-
-/*==========================================
- * Special effects [Valaris]
- *------------------------------------------
- */
-static
-void builtin_specialeffect(ScriptState *st)
-{
- dumb_ptr<block_list> bl = map_id2bl(st->oid);
-
- if (bl == nullptr)
- return;
-
- clif_specialeffect(bl,
- conv_num(st,
- &AARGO2(2)),
- 0);
-
-}
-
-static
-void builtin_specialeffect2(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- if (sd == nullptr)
- return;
-
- clif_specialeffect(sd,
- conv_num(st,
- &AARGO2(2)),
- 0);
-
-}
-
-/*==========================================
- * Nude [Valaris]
- *------------------------------------------
- */
-
-static
-void builtin_nude(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- if (sd == nullptr)
- return;
-
- for (EQUIP i : EQUIPs)
- {
- IOff0 idx = sd->equip_index_maybe[i];
- if (idx.ok())
- pc_unequipitem(sd, idx, CalcStatus::LATER);
- }
- pc_calcstatus(sd, 0);
-
-}
-
-/*==========================================
- * UnequipById [Freeyorp]
- *------------------------------------------
- */
-
-static
-void builtin_unequipbyid(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- if (sd == nullptr)
- return;
-
- EQUIP slot_id = EQUIP(conv_num(st, &AARGO2(2)));
-
- if (slot_id >= EQUIP() && slot_id < EQUIP::COUNT)
- {
- IOff0 idx = sd->equip_index_maybe[slot_id];
- if (idx.ok())
- pc_unequipitem(sd, idx, CalcStatus::LATER);
- }
-
- pc_calcstatus(sd, 0);
-
-}
-
-/*==========================================
- * gmcommand [MouseJstr]
- *
- * suggested on the forums...
- *------------------------------------------
- */
-
-static
-void builtin_gmcommand(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd;
-
- sd = script_rid2sd(st);
- RString cmd = conv_str(st, &AARGO2(2));
-
- is_atcommand(sd->sess, sd, cmd, GmLevel::from(-1U));
-
-}
-
-/*==========================================
- * npcwarp [remoitnane]
- * Move NPC to a new position on the same map.
- *------------------------------------------
- */
-static
-void builtin_npcwarp(ScriptState *st)
-{
- int x, y;
- dumb_ptr<npc_data> nd = nullptr;
-
- x = conv_num(st, &AARGO2(2));
- y = conv_num(st, &AARGO2(3));
- NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARGO2(4))));
- nd = npc_name2id(npc);
-
- if (!nd)
- {
- PRINTF("builtin_npcwarp: no such npc: %s\n"_fmt, npc);
- return;
- }
-
- map_local *m = nd->bl_m;
-
- /* Crude sanity checks. */
- if (m == nullptr || !nd->bl_prev
- || x < 0 || x > m->xs -1
- || y < 0 || y > m->ys - 1)
- return;
-
- npc_enable(npc, 0);
- map_delblock(nd); /* [Freeyorp] */
- nd->bl_x = x;
- nd->bl_y = y;
- map_addblock(nd);
- npc_enable(npc, 1);
-
-}
-
-/*==========================================
- * message [MouseJstr]
- *------------------------------------------
- */
-
-static
-void builtin_message(ScriptState *st)
-{
- CharName player = stringish<CharName>(ZString(conv_str(st, &AARGO2(2))));
- ZString msg = ZString(conv_str(st, &AARGO2(3)));
-
- dumb_ptr<map_session_data> pl_sd = map_nick2sd(player);
- if (pl_sd == nullptr)
- return;
- clif_displaymessage(pl_sd->sess, msg);
-
-}
-
-/*==========================================
- * npctalk (sends message to surrounding
- * area) [Valaris]
- *------------------------------------------
- */
-
-static
-void builtin_npctalk(ScriptState *st)
-{
- dumb_ptr<npc_data> nd = map_id_is_npc(st->oid);
- RString str = conv_str(st, &AARGO2(2));
-
- if (nd)
- {
- clif_message(nd, str);
- }
-}
-
-/*==========================================
- * getlook char info. getlook(arg)
- *------------------------------------------
- */
-static
-void builtin_getlook(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- LOOK type = LOOK(conv_num(st, &AARGO2(2)));
- int val = -1;
- switch (type)
- {
- case LOOK::HAIR: //1
- val = sd->status.hair;
- break;
- case LOOK::WEAPON: //2
- val = static_cast<uint16_t>(sd->status.weapon);
- break;
- case LOOK::HEAD_BOTTOM: //3
- val = unwrap<ItemNameId>(sd->status.head_bottom);
- break;
- case LOOK::HEAD_TOP: //4
- val = unwrap<ItemNameId>(sd->status.head_top);
- break;
- case LOOK::HEAD_MID: //5
- val = unwrap<ItemNameId>(sd->status.head_mid);
- break;
- case LOOK::HAIR_COLOR: //6
- val = sd->status.hair_color;
- break;
- case LOOK::CLOTHES_COLOR: //7
- val = sd->status.clothes_color;
- break;
- case LOOK::SHIELD: //8
- val = unwrap<ItemNameId>(sd->status.shield);
- break;
- case LOOK::SHOES: //9
- break;
- }
-
- push_int<ScriptDataInt>(st->stack, val);
-}
-
-/*==========================================
- * get char save point. argument: 0- map name, 1- x, 2- y
- *------------------------------------------
-*/
-static
-void builtin_getsavepoint(ScriptState *st)
-{
- int x, y, type;
- dumb_ptr<map_session_data> sd;
-
- sd = script_rid2sd(st);
-
- type = conv_num(st, &AARGO2(2));
-
- x = sd->status.save_point.x;
- y = sd->status.save_point.y;
- switch (type)
- {
- case 0:
- {
- RString mapname = sd->status.save_point.map_;
- push_str<ScriptDataStr>(st->stack, mapname);
- }
- break;
- case 1:
- push_int<ScriptDataInt>(st->stack, x);
- break;
- case 2:
- push_int<ScriptDataInt>(st->stack, y);
- break;
- }
-}
-
-/*==========================================
- * areatimer
- *------------------------------------------
- */
-static
-void builtin_areatimer_sub(dumb_ptr<block_list> bl, interval_t tick, NpcEvent event)
-{
- pc_addeventtimer(bl->is_player(), tick, event);
-}
-
-static
-void builtin_areatimer(ScriptState *st)
-{
- int x0, y0, x1, y1;
-
- MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- x0 = conv_num(st, &AARGO2(3));
- y0 = conv_num(st, &AARGO2(4));
- x1 = conv_num(st, &AARGO2(5));
- y1 = conv_num(st, &AARGO2(6));
- interval_t tick = static_cast<interval_t>(conv_num(st, &AARGO2(7)));
- ZString event_ = ZString(conv_str(st, &AARGO2(8)));
- NpcEvent event;
- extract(event_, &event);
-
- map_local *m = map_mapname2mapid(mapname);
- if (m == nullptr)
- return;
-
- map_foreachinarea(std::bind(builtin_areatimer_sub, ph::_1, tick, event),
- m,
- x0, y0,
- x1, y1,
- BL::PC);
-}
-
-/*==========================================
- * Check whether the PC is in the specified rectangle
- *------------------------------------------
- */
-static
-void builtin_isin(ScriptState *st)
-{
- int x1, y1, x2, y2;
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2))));
- x1 = conv_num(st, &AARGO2(3));
- y1 = conv_num(st, &AARGO2(4));
- x2 = conv_num(st, &AARGO2(5));
- y2 = conv_num(st, &AARGO2(6));
-
- if (!sd)
- return;
-
- push_int<ScriptDataInt>(st->stack,
- (sd->bl_x >= x1 && sd->bl_x <= x2)
- && (sd->bl_y >= y1 && sd->bl_y <= y2)
- && (str == sd->bl_m->name_));
-}
-
-// Trigger the shop on a (hopefully) nearby shop NPC
-static
-void builtin_shop(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
- dumb_ptr<npc_data> nd;
-
- if (!sd)
- return;
-
- NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARGO2(2))));
- nd = npc_name2id(name);
- if (!nd)
- {
- PRINTF("builtin_shop: no such npc: %s\n"_fmt, name);
- return;
- }
-
- builtin_close(st);
- clif_npcbuysell(sd, nd->bl_id);
-}
-
-/*==========================================
- * Check whether the PC is dead
- *------------------------------------------
- */
-static
-void builtin_isdead(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- push_int<ScriptDataInt>(st->stack, pc_isdead(sd));
-}
-
-/*========================================
- * Changes a NPC name, and sprite
- *----------------------------------------
- */
-static
-void builtin_fakenpcname(ScriptState *st)
-{
- NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARGO2(2))));
- NpcName newname = stringish<NpcName>(ZString(conv_str(st, &AARGO2(3))));
- Species newsprite = wrap<Species>(static_cast<uint16_t>(conv_num(st, &AARGO2(4))));
- dumb_ptr<npc_data> nd = npc_name2id(name);
- if (!nd)
- {
- PRINTF("builtin_fakenpcname: no such npc: %s\n"_fmt, name);
- return;
- }
- nd->name = newname;
- nd->npc_class = newsprite;
-
- // Refresh this npc
- npc_enable(name, 0);
- npc_enable(name, 1);
-
-}
-
-/*============================
- * Gets the PC's x pos
- *----------------------------
- */
-static
-void builtin_getx(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- push_int<ScriptDataInt>(st->stack, sd->bl_x);
-}
-
-/*============================
- * Gets the PC's y pos
- *----------------------------
- */
-static
-void builtin_gety(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- push_int<ScriptDataInt>(st->stack, sd->bl_y);
-}
-
-/*
- * Get the PC's current map's name
- */
-static
-void builtin_getmap(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd = script_rid2sd(st);
-
- push_str<ScriptDataStr>(st->stack, sd->bl_m->name_);
-}
-
-static
-void builtin_mapexit(ScriptState *)
-{
- runflag = 0;
-}
-
-
-//
-// 実行部main
-//
-/*==========================================
- * コマンドの読み取り
- *------------------------------------------
- */
-static
-ByteCode get_com(ScriptPointer *script)
-{
- if (static_cast<uint8_t>(script->peek()) >= 0x80)
- {
- // synthetic! Does not advance pos yet.
- return ByteCode::INT;
- }
- return script->pop();
-}
-
-/*==========================================
- * 数値の所得
- *------------------------------------------
- */
-static
-int get_num(ScriptPointer *scr)
-{
- int i = 0;
- int j = 0;
- uint8_t val;
- do
- {
- val = static_cast<uint8_t>(scr->pop());
- i += (val & 0x7f) << j;
- j += 6;
- }
- while (val >= 0xc0);
- return i;
-}
-
-/*==========================================
- * スタックから値を取り出す
- *------------------------------------------
- */
-static
-int pop_val(ScriptState *st)
-{
- if (st->stack->stack_datav.empty())
- return 0;
- script_data& back = st->stack->stack_datav.back();
- get_val(st, &back);
- int rv = 0;
- if (auto *u = back.get_if<ScriptDataInt>())
- rv = u->numi;
- st->stack->stack_datav.pop_back();
- return rv;
-}
-
-static
-bool isstr(struct script_data& c)
-{
- return c.is<ScriptDataStr>();
-}
-
-/*==========================================
- * 加算演算子
- *------------------------------------------
- */
-static
-void op_add(ScriptState *st)
-{
- get_val(st, &st->stack->stack_datav.back());
- script_data back = st->stack->stack_datav.back();
- st->stack->stack_datav.pop_back();
-
- script_data& back1 = st->stack->stack_datav.back();
- get_val(st, &back1);
-
- if (!(isstr(back) || isstr(back1)))
- {
- back1.get_if<ScriptDataInt>()->numi += back.get_if<ScriptDataInt>()->numi;
- }
- else
- {
- RString sb = conv_str(st, &back);
- RString sb1 = conv_str(st, &back1);
- MString buf;
- buf += sb1;
- buf += sb;
- back1 = ScriptDataStr{.str= AString(buf)};
- }
-}
-
-/*==========================================
- * 二項演算子(文字列)
- *------------------------------------------
- */
-static
-void op_2str(ScriptState *st, ByteCode op, ZString s1, ZString s2)
-{
- int a = 0;
-
- switch (op)
- {
- case ByteCode::EQ:
- a = s1 == s2;
- break;
- case ByteCode::NE:
- a = s1 != s2;
- break;
- case ByteCode::GT:
- a = s1 > s2;
- break;
- case ByteCode::GE:
- a = s1 >= s2;
- break;
- case ByteCode::LT:
- a = s1 < s2;
- break;
- case ByteCode::LE:
- a = s1 <= s2;
- break;
- default:
- PRINTF("illegal string operater\n"_fmt);
- break;
- }
-
- push_int<ScriptDataInt>(st->stack, a);
-}
-
-/*==========================================
- * 二項演算子(数値)
- *------------------------------------------
- */
-static
-void op_2num(ScriptState *st, ByteCode op, int i1, int i2)
-{
- switch (op)
- {
- case ByteCode::SUB:
- i1 -= i2;
- break;
- case ByteCode::MUL:
- i1 *= i2;
- break;
- case ByteCode::DIV:
- i1 /= i2;
- break;
- case ByteCode::MOD:
- i1 %= i2;
- break;
- case ByteCode::AND:
- i1 &= i2;
- break;
- case ByteCode::OR:
- i1 |= i2;
- break;
- case ByteCode::XOR:
- i1 ^= i2;
- break;
- case ByteCode::LAND:
- i1 = i1 && i2;
- break;
- case ByteCode::LOR:
- i1 = i1 || i2;
- break;
- case ByteCode::EQ:
- i1 = i1 == i2;
- break;
- case ByteCode::NE:
- i1 = i1 != i2;
- break;
- case ByteCode::GT:
- i1 = i1 > i2;
- break;
- case ByteCode::GE:
- i1 = i1 >= i2;
- break;
- case ByteCode::LT:
- i1 = i1 < i2;
- break;
- case ByteCode::LE:
- i1 = i1 <= i2;
- break;
- case ByteCode::R_SHIFT:
- i1 = i1 >> i2;
- break;
- case ByteCode::L_SHIFT:
- i1 = i1 << i2;
- break;
- }
- push_int<ScriptDataInt>(st->stack, i1);
-}
-
-/*==========================================
- * 二項演算子
- *------------------------------------------
- */
-static
-void op_2(ScriptState *st, ByteCode op)
-{
- // pop_val has unfortunate implications here
- script_data d2 = st->stack->stack_datav.back();
- st->stack->stack_datav.pop_back();
- get_val(st, &d2);
- script_data d1 = st->stack->stack_datav.back();
- st->stack->stack_datav.pop_back();
- get_val(st, &d1);
-
- if (isstr(d1) && isstr(d2))
- {
- // ss => op_2str
- op_2str(st, op, d1.get_if<ScriptDataStr>()->str, d2.get_if<ScriptDataStr>()->str);
- }
- else if (!(isstr(d1) || isstr(d2)))
- {
- // ii => op_2num
- op_2num(st, op, d1.get_if<ScriptDataInt>()->numi, d2.get_if<ScriptDataInt>()->numi);
- }
- else
- {
- // si,is => error
- PRINTF("script: op_2: int&str, str&int not allow.\n"_fmt);
- push_int<ScriptDataInt>(st->stack, 0);
- }
-}
-
-/*==========================================
- * 単項演算子
- *------------------------------------------
- */
-static
-void op_1num(ScriptState *st, ByteCode op)
-{
- int i1;
- i1 = pop_val(st);
- switch (op)
- {
- case ByteCode::NEG:
- i1 = -i1;
- break;
- case ByteCode::NOT:
- i1 = ~i1;
- break;
- case ByteCode::LNOT:
- i1 = !i1;
- break;
- }
- push_int<ScriptDataInt>(st->stack, i1);
-}
-
-/*==========================================
- * 関数の実行
- *------------------------------------------
- */
-void run_func(ScriptState *st)
-{
- size_t end_sp = st->stack->stack_datav.size();
- size_t start_sp = end_sp - 1;
- while (!st->stack->stack_datav[start_sp].is<ScriptDataArg>())
- {
- start_sp--;
- if (start_sp == 0)
- {
- if (battle_config.error_log)
- PRINTF("function not found\n"_fmt);
- st->state = ScriptEndState::END;
- return;
- }
- }
- // the func is before the arg
- start_sp--;
- st->start = start_sp;
- st->end = end_sp;
-
- if (!st->stack->stack_datav[st->start].is<ScriptDataFuncRef>())
- {
- PRINTF("run_func: not function and command! \n"_fmt);
- st->state = ScriptEndState::END;
- return;
- }
- size_t func = st->stack->stack_datav[st->start].get_if<ScriptDataFuncRef>()->numi;
-
- if (DEBUG_RUN && battle_config.etc_log)
- {
- PRINTF("run_func : %s\n"_fmt,
- builtin_functions[func].name);
- PRINTF("stack dump :"_fmt);
- for (script_data& d : st->stack->stack_datav)
- {
- MATCH (d)
- {
- CASE (const ScriptDataInt&, u)
- {
- PRINTF(" int(%d)"_fmt, u.numi);
- }
- CASE (const ScriptDataRetInfo&, u)
- {
- PRINTF(" retinfo(%p)"_fmt, static_cast<const void *>(u.script));
- }
- CASE (const ScriptDataParam&, u)
- {
- PRINTF(" param(%d)"_fmt, u.reg.sp());
- }
- CASE (const ScriptDataVariable&, u)
- {
- PRINTF(" name(%s)[%d]"_fmt, variable_names.outtern(u.reg.base()), u.reg.index());
- }
- CASE (const ScriptDataArg&, u)
- {
- (void)u;
- PRINTF(" arg"_fmt);
- }
- CASE (const ScriptDataPos&, u)
- {
- (void)u;
- PRINTF(" pos(%d)"_fmt, u.numi);
- }
- CASE (const ScriptDataStr&, u)
- {
- (void)u;
- PRINTF(" str(%s)"_fmt, u.str);
- }
- CASE (const ScriptDataFuncRef&, u)
- {
- (void)u;
- PRINTF(" func(%s)"_fmt, builtin_functions[u.numi].name);
- }
- }
- }
- PRINTF("\n"_fmt);
- }
- builtin_functions[func].func(st);
-
- pop_stack(st->stack, start_sp, end_sp);
-
- if (st->state == ScriptEndState::RETFUNC)
- {
- // ユーザー定義関数からの復帰
- int olddefsp = st->defsp;
-
- pop_stack(st->stack, st->defsp, start_sp); // 復帰に邪魔なスタック削除
- if (st->defsp < 4
- || !st->stack->stack_datav[st->defsp - 1].is<ScriptDataRetInfo>())
- {
- PRINTF("script:run_func (return) return without callfunc or callsub!\n"_fmt);
- st->state = ScriptEndState::END;
- return;
- }
- assert (olddefsp == st->defsp); // pretty sure it hasn't changed yet
- st->scriptp.code = conv_script(st, &st->stack->stack_datav[olddefsp - 1]); // スクリプトを復元
- st->scriptp.pos = conv_num(st, &st->stack->stack_datav[olddefsp - 2]); // スクリプト位置の復元
- st->defsp = conv_num(st, &st->stack->stack_datav[olddefsp - 3]); // 基準スタックポインタを復元
- // Number of arguments.
- int i = conv_num(st, &st->stack->stack_datav[olddefsp - 4]); // 引数の数所得
- assert (i == 0);
-
- pop_stack(st->stack, olddefsp - 4 - i, olddefsp); // 要らなくなったスタック(引数と復帰用データ)削除
-
- st->state = ScriptEndState::GOTO;
- }
-}
-
-// pretend it's external so this can be called in the debugger
-void dump_script(const ScriptBuffer *script);
-void dump_script(const ScriptBuffer *script)
-{
- ScriptPointer scriptp(script, 0);
- while (scriptp.pos < reinterpret_cast<const std::vector<ByteCode> *>(script)->size())
- {
- PRINTF("%6zu: "_fmt, scriptp.pos);
- switch (ByteCode c = get_com(&scriptp))
- {
- case ByteCode::EOL:
- PRINTF("EOL\n"_fmt); // extra newline between functions
- break;
- case ByteCode::INT:
- // synthesized!
- PRINTF("INT %d"_fmt, get_num(&scriptp));
- break;
-
- case ByteCode::POS:
- case ByteCode::VARIABLE:
- case ByteCode::FUNC_REF:
- case ByteCode::PARAM:
- {
- int arg = 0;
- arg |= static_cast<uint8_t>(scriptp.pop()) << 0;
- arg |= static_cast<uint8_t>(scriptp.pop()) << 8;
- arg |= static_cast<uint8_t>(scriptp.pop()) << 16;
- switch(c)
- {
- case ByteCode::POS:
- PRINTF("POS %d"_fmt, arg);
- break;
- case ByteCode::VARIABLE:
- PRINTF("VARIABLE %s"_fmt, variable_names.outtern(arg));
- break;
- case ByteCode::FUNC_REF:
- PRINTF("FUNC_REF %s"_fmt, builtin_functions[arg].name);
- break;
- case ByteCode::PARAM:
- PRINTF("PARAM SP::#%d (sorry)"_fmt, arg);
- break;
- }
- }
- break;
- case ByteCode::ARG:
- PRINTF("ARG"_fmt);
- break;
- case ByteCode::STR:
- PRINTF("STR \"%s\""_fmt, scriptp.pops());
- break;
- case ByteCode::FUNC:
- PRINTF("FUNC"_fmt);
- break;
-
- case ByteCode::ADD:
- PRINTF("ADD"_fmt);
- break;
- case ByteCode::SUB:
- PRINTF("SUB"_fmt);
- break;
- case ByteCode::MUL:
- PRINTF("MUL"_fmt);
- break;
- case ByteCode::DIV:
- PRINTF("DIV"_fmt);
- break;
- case ByteCode::MOD:
- PRINTF("MOD"_fmt);
- break;
- case ByteCode::EQ:
- PRINTF("EQ"_fmt);
- break;
- case ByteCode::NE:
- PRINTF("NE"_fmt);
- break;
- case ByteCode::GT:
- PRINTF("GT"_fmt);
- break;
- case ByteCode::GE:
- PRINTF("GE"_fmt);
- break;
- case ByteCode::LT:
- PRINTF("LT"_fmt);
- break;
- case ByteCode::LE:
- PRINTF("LE"_fmt);
- break;
- case ByteCode::AND:
- PRINTF("AND"_fmt);
- break;
- case ByteCode::OR:
- PRINTF("OR"_fmt);
- break;
- case ByteCode::XOR:
- PRINTF("XOR"_fmt);
- break;
- case ByteCode::LAND:
- PRINTF("LAND"_fmt);
- break;
- case ByteCode::LOR:
- PRINTF("LOR"_fmt);
- break;
- case ByteCode::R_SHIFT:
- PRINTF("R_SHIFT"_fmt);
- break;
- case ByteCode::L_SHIFT:
- PRINTF("L_SHIFT"_fmt);
- break;
- case ByteCode::NEG:
- PRINTF("NEG"_fmt);
- break;
- case ByteCode::NOT:
- PRINTF("NOT"_fmt);
- break;
- case ByteCode::LNOT:
- PRINTF("LNOT"_fmt);
- break;
-
- case ByteCode::NOP:
- PRINTF("NOP"_fmt);
- break;
-
- default:
- PRINTF("??? %d"_fmt, c);
- break;
- }
- PRINTF("\n"_fmt);
- }
-}
-
-/*==========================================
- * スクリプトの実行メイン部分
- *------------------------------------------
- */
-static
-void run_script_main(ScriptState *st, const ScriptBuffer *rootscript)
-{
- int cmdcount = script_config.check_cmdcount;
- int gotocount = script_config.check_gotocount;
- struct script_stack *stack = st->stack;
-
- st->defsp = stack->stack_datav.size();
-
- int rerun_pos = st->scriptp.pos;
- st->state = ScriptEndState::ZERO;
- while (st->state == ScriptEndState::ZERO)
- {
- switch (ByteCode c = get_com(&st->scriptp))
- {
- case ByteCode::EOL:
- if (stack->stack_datav.size() != st->defsp)
- {
- if (true)
- PRINTF("stack.sp (%zu) != default (%d)\n"_fmt,
- stack->stack_datav.size(),
- st->defsp);
- abort();
- }
- rerun_pos = st->scriptp.pos;
- break;
- case ByteCode::INT:
- // synthesized!
- push_int<ScriptDataInt>(stack, get_num(&st->scriptp));
- break;
-
- case ByteCode::POS:
- case ByteCode::VARIABLE:
- case ByteCode::FUNC_REF:
- case ByteCode::PARAM:
- // Note that these 3 have *very* different meanings,
- // despite being encoded similarly.
- {
- int arg = 0;
- arg |= static_cast<uint8_t>(st->scriptp.pop()) << 0;
- arg |= static_cast<uint8_t>(st->scriptp.pop()) << 8;
- arg |= static_cast<uint8_t>(st->scriptp.pop()) << 16;
- switch(c)
- {
- case ByteCode::POS:
- push_int<ScriptDataPos>(stack, arg);
- break;
- case ByteCode::VARIABLE:
- push_reg<ScriptDataVariable>(stack, SIR::from(arg));
- break;
- case ByteCode::FUNC_REF:
- push_int<ScriptDataFuncRef>(stack, arg);
- break;
- case ByteCode::PARAM:
- SP arg_sp = static_cast<SP>(arg);
- push_reg<ScriptDataParam>(stack, SIR::from(arg_sp));
- break;
- }
- }
- break;
- case ByteCode::ARG:
- push_int<ScriptDataArg>(stack, 0);
- break;
- case ByteCode::STR:
- push_str<ScriptDataStr>(stack, st->scriptp.pops());
- break;
- case ByteCode::FUNC:
- run_func(st);
- if (st->state == ScriptEndState::GOTO)
- {
- rerun_pos = st->scriptp.pos;
- st->state = ScriptEndState::ZERO;
- if (gotocount > 0 && (--gotocount) <= 0)
- {
- PRINTF("run_script: infinity loop !\n"_fmt);
- st->state = ScriptEndState::END;
- }
- }
- break;
-
- case ByteCode::ADD:
- op_add(st);
- break;
-
- case ByteCode::SUB:
- case ByteCode::MUL:
- case ByteCode::DIV:
- case ByteCode::MOD:
- case ByteCode::EQ:
- case ByteCode::NE:
- case ByteCode::GT:
- case ByteCode::GE:
- case ByteCode::LT:
- case ByteCode::LE:
- case ByteCode::AND:
- case ByteCode::OR:
- case ByteCode::XOR:
- case ByteCode::LAND:
- case ByteCode::LOR:
- case ByteCode::R_SHIFT:
- case ByteCode::L_SHIFT:
- op_2(st, c);
- break;
-
- case ByteCode::NEG:
- case ByteCode::NOT:
- case ByteCode::LNOT:
- op_1num(st, c);
- break;
-
- case ByteCode::NOP:
- st->state = ScriptEndState::END;
- break;
-
- default:
- if (battle_config.error_log)
- PRINTF("unknown command : %d @ %zu\n"_fmt,
- c, st->scriptp.pos);
- st->state = ScriptEndState::END;
- break;
- }
- if (cmdcount > 0 && (--cmdcount) <= 0)
- {
- PRINTF("run_script: infinity loop !\n"_fmt);
- st->state = ScriptEndState::END;
- }
- }
- switch (st->state)
- {
- case ScriptEndState::STOP:
- break;
- case ScriptEndState::END:
- {
- dumb_ptr<map_session_data> sd = map_id2sd(st->rid);
- st->scriptp.code = nullptr;
- st->scriptp.pos = -1;
- if (sd && sd->npc_id == st->oid)
- npc_event_dequeue(sd);
- }
- break;
- case ScriptEndState::RERUNLINE:
- st->scriptp.pos = rerun_pos;
- break;
- }
-
- if (st->state != ScriptEndState::END)
- {
- // 再開するためにスタック情報を保存
- dumb_ptr<map_session_data> sd = map_id2sd(st->rid);
- if (sd)
- {
- sd->npc_stackbuf = stack->stack_datav;
- sd->npc_script = st->scriptp.code;
- // sd->npc_pos is set later ... ???
- sd->npc_scriptroot = rootscript;
- }
- }
-}
-
-/*==========================================
- * スクリプトの実行
- *------------------------------------------
- */
-int run_script(ScriptPointer sp, BlockId rid, BlockId oid)
-{
- return run_script_l(sp, rid, oid, nullptr);
-}
-
-int run_script_l(ScriptPointer sp, BlockId rid, BlockId oid,
- Slice<argrec_t> args)
-{
- struct script_stack stack;
- ScriptState st;
- dumb_ptr<map_session_data> sd = map_id2sd(rid);
- const ScriptBuffer *rootscript = sp.code;
- int i;
- if (sp.code == nullptr || sp.pos >> 24)
- return -1;
-
- if (sd && !sd->npc_stackbuf.empty() && sd->npc_scriptroot == rootscript)
- {
- // 前回のスタックを復帰
- sp.code = sd->npc_script;
- stack.stack_datav = std::move(sd->npc_stackbuf);
- }
- st.stack = &stack;
- st.scriptp = sp;
- st.rid = rid;
- st.oid = oid;
- for (i = 0; i < args.size(); i++)
- {
- if (args[i].name.back() == '$')
- pc_setregstr(sd, SIR::from(variable_names.intern(args[i].name)), args[i].v.s);
- else
- pc_setreg(sd, SIR::from(variable_names.intern(args[i].name)), args[i].v.i);
- }
- run_script_main(&st, rootscript);
-
- stack.stack_datav.clear();
- return st.scriptp.pos;
-}
-
-/*==========================================
- * マップ変数の変更
- *------------------------------------------
- */
-void mapreg_setreg(SIR reg, int val)
-{
- mapreg_db.put(reg, val);
-
- mapreg_dirty = 1;
-}
-
-/*==========================================
- * 文字列型マップ変数の変更
- *------------------------------------------
- */
-void mapreg_setregstr(SIR reg, XString str)
-{
- if (!str)
- mapregstr_db.erase(reg);
- else
- mapregstr_db.insert(reg, str);
-
- mapreg_dirty = 1;
-}
-
-/*==========================================
- * 永続的マップ変数の読み込み
- *------------------------------------------
- */
-static
-void script_load_mapreg(void)
-{
- io::ReadFile in(mapreg_txt);
-
- if (!in.is_open())
- return;
-
- AString line;
- while (in.getline(line))
- {
- XString buf1, buf2;
- int index = 0;
- if (extract(line,
- record<'\t'>(
- record<','>(&buf1, &index),
- &buf2))
- || extract(line,
- record<'\t'>(
- record<','>(&buf1),
- &buf2)))
- {
- int s = variable_names.intern(buf1);
- SIR key = SIR::from(s, index);
- if (buf1.back() == '$')
- {
- mapregstr_db.insert(key, buf2);
- }
- else
- {
- int v;
- if (!extract(buf2, &v))
- goto borken;
- mapreg_db.put(key, v);
- }
- }
- else
- {
- borken:
- PRINTF("%s: %s broken data !\n"_fmt, mapreg_txt, AString(buf1));
- continue;
- }
- }
- mapreg_dirty = 0;
-}
-
-/*==========================================
- * 永続的マップ変数の書き込み
- *------------------------------------------
- */
-static
-void script_save_mapreg_intsub(SIR key, int data, io::WriteFile& fp)
-{
- int num = key.base(), i = key.index();
- ZString name = variable_names.outtern(num);
- if (name[1] != '@')
- {
- if (i == 0)
- FPRINTF(fp, "%s\t%d\n"_fmt, name, data);
- else
- FPRINTF(fp, "%s,%d\t%d\n"_fmt, name, i, data);
- }
-}
-
-static
-void script_save_mapreg_strsub(SIR key, ZString data, io::WriteFile& fp)
-{
- int num = key.base(), i = key.index();
- ZString name = variable_names.outtern(num);
- if (name[1] != '@')
- {
- if (i == 0)
- FPRINTF(fp, "%s\t%s\n"_fmt, name, data);
- else
- FPRINTF(fp, "%s,%d\t%s\n"_fmt, name, i, data);
- }
-}
-
-static
-void script_save_mapreg(void)
-{
- io::WriteLock fp(mapreg_txt);
- if (!fp.is_open())
- return;
- for (auto& pair : mapreg_db)
- script_save_mapreg_intsub(pair.first, pair.second, fp);
- for (auto& pair : mapregstr_db)
- script_save_mapreg_strsub(pair.first, pair.second, fp);
- mapreg_dirty = 0;
-}
-
-static
-void script_autosave_mapreg(TimerData *, tick_t)
-{
- if (mapreg_dirty)
- script_save_mapreg();
-}
-
-void do_final_script(void)
-{
- if (mapreg_dirty >= 0)
- script_save_mapreg();
-
- mapreg_db.clear();
- mapregstr_db.clear();
- scriptlabel_db.clear();
- userfunc_db.clear();
-
- str_datam.clear();
-}
-
-/*==========================================
- * 初期化
- *------------------------------------------
- */
-void do_init_script(void)
-{
- script_load_mapreg();
-
- Timer(gettick() + MAPREG_AUTOSAVE_INTERVAL,
- script_autosave_mapreg,
- MAPREG_AUTOSAVE_INTERVAL
- ).detach();
-}
-
-#define BUILTIN(func, args, ret) \
-{builtin_##func, #func ## _s, args, ret}
-
-BuiltinFunction builtin_functions[] =
-{
- BUILTIN(mes, "s"_s, '\0'),
- BUILTIN(goto, "L"_s, '\0'),
- BUILTIN(callfunc, "F"_s, '\0'),
- BUILTIN(callsub, "L"_s, '\0'),
- BUILTIN(return, ""_s, '\0'),
- BUILTIN(next, ""_s, '\0'),
- BUILTIN(close, ""_s, '\0'),
- BUILTIN(close2, ""_s, '\0'),
- BUILTIN(menu, "sL**"_s, '\0'),
- BUILTIN(rand, "i?"_s, 'i'),
- BUILTIN(isat, "Mxy"_s, 'i'),
- BUILTIN(warp, "Mxy"_s, '\0'),
- BUILTIN(areawarp, "MxyxyMxy"_s, '\0'),
- BUILTIN(heal, "ii"_s, '\0'),
- BUILTIN(itemheal, "ii"_s, '\0'),
- BUILTIN(percentheal, "ii"_s, '\0'),
- BUILTIN(input, "N"_s, '\0'),
- BUILTIN(if, "iF*"_s, '\0'),
- BUILTIN(set, "Ne"_s, '\0'),
- BUILTIN(setarray, "Ne*"_s, '\0'),
- BUILTIN(cleararray, "Nei"_s, '\0'),
- BUILTIN(getarraysize, "N"_s, 'i'),
- BUILTIN(getelementofarray, "Ni"_s, '.'),
- BUILTIN(setlook, "ii"_s, '\0'),
- BUILTIN(countitem, "I"_s, 'i'),
- BUILTIN(checkweight, "Ii"_s, 'i'),
- BUILTIN(getitem, "Ii??"_s, '\0'),
- BUILTIN(makeitem, "IiMxy"_s, '\0'),
- BUILTIN(delitem, "Ii"_s, '\0'),
- BUILTIN(readparam, "i?"_s, 'i'),
- BUILTIN(getcharid, "i?"_s, 'i'),
- BUILTIN(strcharinfo, "i"_s, 's'),
- BUILTIN(getequipid, "i"_s, 'i'),
- BUILTIN(getequipname, "i"_s, 's'),
- BUILTIN(statusup2, "ii"_s, '\0'),
- BUILTIN(bonus, "ii"_s, '\0'),
- BUILTIN(bonus2, "iii"_s, '\0'),
- BUILTIN(skill, "ii?"_s, '\0'),
- BUILTIN(setskill, "ii"_s, '\0'),
- BUILTIN(getskilllv, "i"_s, 'i'),
- BUILTIN(getgmlevel, ""_s, 'i'),
- BUILTIN(end, ""_s, '\0'),
- BUILTIN(getopt2, ""_s, 'i'),
- BUILTIN(setopt2, "i"_s, '\0'),
- BUILTIN(savepoint, "Mxy"_s, '\0'),
- BUILTIN(gettimetick, "i"_s, 'i'),
- BUILTIN(gettime, "i"_s, 'i'),
- BUILTIN(openstorage, ""_s, '\0'),
- BUILTIN(getexp, "ii"_s, '\0'),
- BUILTIN(monster, "Mxysmi?"_s, '\0'),
- BUILTIN(areamonster, "Mxyxysmi?"_s, '\0'),
- BUILTIN(killmonster, "ME"_s, '\0'),
- BUILTIN(killmonsterall, "M"_s, '\0'),
- BUILTIN(donpcevent, "E"_s, '\0'),
- BUILTIN(addtimer, "tE"_s, '\0'),
- BUILTIN(initnpctimer, ""_s, '\0'),
- BUILTIN(startnpctimer, "?"_s, '\0'),
- BUILTIN(stopnpctimer, ""_s, '\0'),
- BUILTIN(getnpctimer, "i"_s, 'i'),
- BUILTIN(setnpctimer, "i"_s, '\0'),
- BUILTIN(announce, "si"_s, '\0'),
- BUILTIN(mapannounce, "Msi"_s, '\0'),
- BUILTIN(getusers, "i"_s, 'i'),
- BUILTIN(getmapusers, "M"_s, 'i'),
- BUILTIN(getareausers, "Mxyxy?"_s, 'i'),
- BUILTIN(getareadropitem, "Mxyxyi?"_s, 'i'),
- BUILTIN(enablenpc, "s"_s, '\0'),
- BUILTIN(disablenpc, "s"_s, '\0'),
- BUILTIN(sc_start, "iTi?"_s, '\0'),
- BUILTIN(sc_end, "i"_s, '\0'),
- BUILTIN(sc_check, "i"_s, 'i'),
- BUILTIN(debugmes, "s"_s, '\0'),
- BUILTIN(resetstatus, ""_s, '\0'),
- BUILTIN(changesex, ""_s, '\0'),
- BUILTIN(attachrid, "i"_s, 'i'),
- BUILTIN(detachrid, ""_s, '\0'),
- BUILTIN(isloggedin, "i"_s, 'i'),
- BUILTIN(setmapflag, "Mi"_s, '\0'),
- BUILTIN(removemapflag, "Mi"_s, '\0'),
- BUILTIN(getmapflag, "Mi"_s, 'i'),
- BUILTIN(pvpon, "M"_s, '\0'),
- BUILTIN(pvpoff, "M"_s, '\0'),
- BUILTIN(emotion, "i"_s, '\0'),
- BUILTIN(mapwarp, "MMxy"_s, '\0'),
- BUILTIN(cmdothernpc, "ss"_s, '\0'),
- BUILTIN(mobcount, "ME"_s, 'i'),
- BUILTIN(marriage, "P"_s, 'i'),
- BUILTIN(divorce, ""_s, 'i'),
- BUILTIN(getitemname, "I"_s, 's'),
- BUILTIN(getspellinvocation, "s"_s, 's'),
- BUILTIN(getpartnerid2, ""_s, 'i'),
- BUILTIN(getinventorylist, ""_s, '\0'),
- BUILTIN(getactivatedpoolskilllist, ""_s, '\0'),
- BUILTIN(getunactivatedpoolskilllist, ""_s, '\0'),
- BUILTIN(poolskill, "i"_s, '\0'),
- BUILTIN(unpoolskill, "i"_s, '\0'),
- BUILTIN(misceffect, "i?"_s, '\0'),
- BUILTIN(specialeffect, "i"_s, '\0'),
- BUILTIN(specialeffect2, "i"_s, '\0'),
- BUILTIN(nude, ""_s, '\0'),
- BUILTIN(unequipbyid, "i"_s, '\0'),
- BUILTIN(gmcommand, "s"_s, '\0'),
- BUILTIN(npcwarp, "xys"_s, '\0'),
- BUILTIN(message, "Ps"_s, '\0'),
- BUILTIN(npctalk, "s"_s, '\0'),
- BUILTIN(getlook, "i"_s, 'i'),
- BUILTIN(getsavepoint, "i"_s, '.'),
- BUILTIN(areatimer, "MxyxytE"_s, '\0'),
- BUILTIN(isin, "Mxyxy"_s, 'i'),
- BUILTIN(shop, "s"_s, '\0'),
- BUILTIN(isdead, ""_s, 'i'),
- BUILTIN(fakenpcname, "ssi"_s, '\0'),
- BUILTIN(getx, ""_s, 'i'),
- BUILTIN(gety, ""_s, 'i'),
- BUILTIN(getmap, ""_s, 's'),
- BUILTIN(mapexit, ""_s, '\0'),
- {nullptr, ""_s, ""_s, '\0'},
-};
-
-void set_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e, int val)
-{
- size_t k = variable_names.intern(var);
- SIR reg = SIR::from(k, e);
- set_reg(sd, VariableCode::VARIABLE, reg, val);
-}
-void set_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e, XString val)
-{
- size_t k = variable_names.intern(var);
- SIR reg = SIR::from(k, e);
- set_reg(sd, VariableCode::VARIABLE, reg, val);
-}
-int get_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e)
-{
- size_t k = variable_names.intern(var);
- SIR reg = SIR::from(k, e);
- struct script_data dat = ScriptDataVariable{.reg= reg};
- get_val(sd, &dat);
- if (auto *u = dat.get_if<ScriptDataInt>())
- return u->numi;
- PRINTF("Warning: you lied about the type and I'm too lazy to fix it!"_fmt);
- return 0;
-}
-ZString get_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e)
-{
- size_t k = variable_names.intern(var);
- SIR reg = SIR::from(k, e);
- struct script_data dat = ScriptDataVariable{.reg= reg};
- get_val(sd, &dat);
- if (auto *u = dat.get_if<ScriptDataStr>())
- // this is almost certainly a memory leak after CONSTSTR removal
- return u->str;
- PRINTF("Warning: you lied about the type and I can't fix it!"_fmt);
- return ZString();
-}
-} // namespace tmwa
diff --git a/src/map/script.hpp b/src/map/script.hpp
deleted file mode 100644
index d5200a6..0000000
--- a/src/map/script.hpp
+++ /dev/null
@@ -1,277 +0,0 @@
-#pragma once
-// script.hpp - EAthena script frontend, engine, and library.
-//
-// Copyright © ????-2004 Athena Dev Teams
-// Copyright © 2004-2011 The Mana World Development Team
-// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "fwd.hpp"
-
-#include <cstdint>
-
-#include <vector>
-
-#include "../range/slice.hpp"
-
-#include "../strings/zstring.hpp"
-
-#include "../generic/db.hpp"
-#include "../generic/dumb_ptr.hpp"
-
-#include "../sexpr/variant.hpp"
-
-#include "../mmo/ids.hpp"
-
-#include "clif.t.hpp"
-#include "map.t.hpp"
-
-
-namespace tmwa
-{
-enum class VariableCode : uint8_t
-{
- PARAM,
- VARIABLE,
-};
-
-enum class StringCode : uint8_t
-{
- NOP, POS, INT, PARAM, FUNC,
- VARIABLE,
-};
-
-enum class ByteCode : uint8_t
-{
- // types and specials
- // Note that 'INT' is synthetic, and does not occur in the data stream
- NOP, POS, INT, PARAM, FUNC, STR, ARG,
- VARIABLE, EOL,
-
- // unary and binary operators
- LOR, LAND, LE, LT, GE, GT, EQ, NE,
- XOR, OR, AND, ADD, SUB, MUL, DIV, MOD,
- NEG, LNOT, NOT, R_SHIFT, L_SHIFT,
-
- // additions
- // needed because FUNC is used for the actual call
- FUNC_REF,
-};
-
-struct str_data_t;
-
-class ScriptBuffer
-{
- typedef ZString::iterator ZSit;
-
- std::vector<ByteCode> script_buf;
-public:
- // construction methods used only by script.cpp
- void add_scriptc(ByteCode a);
- void add_scriptb(uint8_t a);
- void add_scripti(uint32_t a);
- void add_scriptl(str_data_t *a);
- void set_label(str_data_t *ld, int pos_);
- ZSit parse_simpleexpr(ZSit p);
- ZSit parse_subexpr(ZSit p, int limit);
- ZSit parse_expr(ZSit p);
- ZSit parse_line(ZSit p, bool *canstep);
- void parse_script(ZString src, int line, bool implicit_end);
-
- // consumption methods used only by script.cpp
- ByteCode operator[](size_t i) const { return script_buf[i]; }
- ZString get_str(size_t i) const
- {
- return ZString(strings::really_construct_from_a_pointer, reinterpret_cast<const char *>(&script_buf[i]), nullptr);
- }
-
- // method used elsewhere
-};
-
-struct ScriptPointer
-{
- const ScriptBuffer *code;
- size_t pos;
-
- ScriptPointer()
- : code()
- , pos()
- {}
-
- ScriptPointer(const ScriptBuffer *c, size_t p)
- : code(c)
- , pos(p)
- {}
-
- ByteCode peek() const { return (*code)[pos]; }
- ByteCode pop() { return (*code)[pos++]; }
- ZString pops()
- {
- ZString rv = code->get_str(pos);
- pos += rv.size();
- ++pos;
- return rv;
- }
-};
-
-// internal
-class SIR
-{
- uint32_t impl;
- SIR(SP v)
- : impl(static_cast<uint32_t>(v))
- {}
- SIR(unsigned v, uint8_t i)
- : impl((i << 24) | v)
- {}
-public:
- SIR() : impl() {}
-
- unsigned base() const { return impl & 0x00ffffff; }
- uint8_t index() const { return impl >> 24; }
- SIR iplus(uint8_t i) const { return SIR(base(), index() + i); }
- static SIR from(unsigned v, uint8_t i=0) { return SIR(v, i); }
-
- SP sp() const { return static_cast<SP>(impl); }
- static SIR from(SP v) { return SIR(v); }
-
- friend bool operator == (SIR l, SIR r) { return l.impl == r.impl; }
- friend bool operator < (SIR l, SIR r) { return l.impl < r.impl; }
-};
-
-struct ScriptDataPos
-{
- int numi;
-};
-struct ScriptDataInt
-{
- int numi;
-};
-struct ScriptDataParam
-{
- SIR reg;
-};
-struct ScriptDataStr
-{
- RString str;
-};
-struct ScriptDataArg
-{
- int numi;
-};
-struct ScriptDataVariable
-{
- SIR reg;
-};
-struct ScriptDataRetInfo
-{
- // Not a ScriptPointer - pos is stored in a separate slot,
- // to avoid exploding the struct for everyone.
- const ScriptBuffer *script;
-};
-struct ScriptDataFuncRef
-{
- int numi;
-};
-
-using ScriptDataVariantBase = Variant<
- ScriptDataPos,
- ScriptDataInt,
- ScriptDataParam,
- ScriptDataStr,
- ScriptDataArg,
- ScriptDataVariable,
- ScriptDataRetInfo,
- ScriptDataFuncRef
->;
-struct script_data : ScriptDataVariantBase
-{
- script_data() = delete;
- // TODO see if I can delete the copy ctor/assign instead of defaulting
- script_data(script_data&&) = default;
- script_data(const script_data&) = default /*delete*/;
- script_data& operator = (script_data&&) = default;
- script_data& operator = (const script_data&) = default /*delete*/;
-
- script_data(ScriptDataPos v) : ScriptDataVariantBase(std::move(v)) {}
- script_data(ScriptDataInt v) : ScriptDataVariantBase(std::move(v)) {}
- script_data(ScriptDataParam v) : ScriptDataVariantBase(std::move(v)) {}
- script_data(ScriptDataStr v) : ScriptDataVariantBase(std::move(v)) {}
- script_data(ScriptDataArg v) : ScriptDataVariantBase(std::move(v)) {}
- script_data(ScriptDataVariable v) : ScriptDataVariantBase(std::move(v)) {}
- script_data(ScriptDataRetInfo v) : ScriptDataVariantBase(std::move(v)) {}
- script_data(ScriptDataFuncRef v) : ScriptDataVariantBase(std::move(v)) {}
-};
-
-struct script_stack
-{
- std::vector<struct script_data> stack_datav;
-};
-
-enum class ScriptEndState;
-// future improvements coming!
-class ScriptState
-{
-public:
- struct script_stack *stack;
- int start, end;
- ScriptEndState state;
- BlockId rid, oid;
- ScriptPointer scriptp, new_scriptp;
- int defsp, new_defsp;
-};
-
-std::unique_ptr<const ScriptBuffer> parse_script(ZString, int, bool implicit_end);
-
-struct argrec_t
-{
- ZString name;
- union _aru
- {
- int i;
- ZString s;
-
- _aru(int n) : i(n) {}
- _aru(ZString z) : s(z) {}
- } v;
-
- argrec_t(ZString n, int i) : name(n), v(i) {}
- argrec_t(ZString n, ZString z) : name(n), v(z) {}
-};
-int run_script_l(ScriptPointer, BlockId, BlockId, Slice<argrec_t> args);
-int run_script(ScriptPointer, BlockId, BlockId);
-
-extern
-Map<ScriptLabel, int> scriptlabel_db;
-extern
-UPMap<RString, const ScriptBuffer> userfunc_db;
-
-void do_init_script(void);
-void do_final_script(void);
-
-extern AString mapreg_txt;
-
-extern int script_errors;
-
-bool read_constdb(ZString filename);
-
-void set_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e, int val);
-void set_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e, XString val);
-
-int get_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e);
-ZString get_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e);
-} // namespace tmwa
diff --git a/src/map/script.py b/src/map/script.py
deleted file mode 100644
index a5010cd..0000000
--- a/src/map/script.py
+++ /dev/null
@@ -1,25 +0,0 @@
-class script_data(object):
- enabled = True
-
- test_extra = '''
- using tmwa::operator "" _s;
- '''
-
- tests = [
- ('tmwa::script_data(tmwa::ScriptDataPos{42})',
- '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataPos) = {numi = 42}}, <No data fields>}'),
- ('tmwa::script_data(tmwa::ScriptDataInt{123})',
- '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataInt) = {numi = 123}}, <No data fields>}'),
- ('tmwa::script_data(tmwa::ScriptDataParam{tmwa::SIR()})',
- '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataParam) = {reg = {impl = 0}}}, <No data fields>}'),
- ('tmwa::script_data(tmwa::ScriptDataStr{"Hello"_s})',
- '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataStr) = {str = "Hello"}}, <No data fields>}'),
- ('tmwa::script_data(tmwa::ScriptDataArg{0})',
- '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataArg) = {numi = 0}}, <No data fields>}'),
- ('tmwa::script_data(tmwa::ScriptDataVariable{tmwa::SIR()})',
- '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataVariable) = {reg = {impl = 0}}}, <No data fields>}'),
- ('tmwa::script_data(tmwa::ScriptDataRetInfo{static_cast<const tmwa::ScriptBuffer *>(nullptr)})',
- '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataRetInfo) = {script = 0x0}}, <No data fields>}'),
- ('tmwa::script_data(tmwa::ScriptDataFuncRef{404})',
- '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataFuncRef) = {numi = 404}}, <No data fields>}'),
- ]
diff --git a/src/map/skill-pools.cpp b/src/map/skill-pools.cpp
index 89bf426..dfc70b0 100644
--- a/src/map/skill-pools.cpp
+++ b/src/map/skill-pools.cpp
@@ -21,9 +21,12 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
+
+#include "../mmo/cxxstdio_enums.hpp"
#include "battle.hpp"
+#include "consts.hpp"
+#include "globals.hpp"
#include "pc.hpp"
#include "../poison.hpp"
@@ -31,26 +34,18 @@
namespace tmwa
{
-Array<SkillID, MAX_POOL_SKILLS> skill_pool_skills;
-int skill_pool_skills_size = 0;
-
+namespace map
+{
void skill_pool_register(SkillID id)
{
- if (skill_pool_skills_size + 1 >= MAX_POOL_SKILLS)
- {
- FPRINTF(stderr,
- "Too many pool skills! Increase MAX_POOL_SKILLS and recompile."_fmt);
- return;
- }
-
- skill_pool_skills[skill_pool_skills_size++] = id;
+ skill_pool_skills.push_back(id);
}
int skill_pool(dumb_ptr<map_session_data> sd, SkillID *skills)
{
int i, count = 0;
- for (i = 0; count < MAX_SKILL_POOL && i < skill_pool_skills_size; i++)
+ for (i = 0; count < MAX_SKILL_POOL && i < skill_pool_skills.size(); i++)
{
SkillID skill_id = skill_pool_skills[i];
if (bool(sd->status.skill[skill_id].flags & SkillFlags::POOL_ACTIVATED))
@@ -147,4 +142,5 @@ int skill_power_bl(dumb_ptr<block_list> bl, SkillID skill)
else
return 0;
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/skill-pools.hpp b/src/map/skill-pools.hpp
index c8890e8..0f75e8e 100644
--- a/src/map/skill-pools.hpp
+++ b/src/map/skill-pools.hpp
@@ -23,4 +23,7 @@
namespace tmwa
{
+namespace map
+{
+} // namespace map
} // namespace tmwa
diff --git a/src/map/skill.cpp b/src/map/skill.cpp
index add6a42..8a397a3 100644
--- a/src/map/skill.cpp
+++ b/src/map/skill.cpp
@@ -39,16 +39,18 @@
#include "../generic/random.hpp"
#include "../io/cxxstdio.hpp"
-#include "../io/cxxstdio_enums.hpp"
+#include "../io/extract.hpp"
#include "../io/read.hpp"
#include "../net/timer.hpp"
-#include "../mmo/extract.hpp"
+#include "../mmo/cxxstdio_enums.hpp"
#include "../mmo/extract_enums.hpp"
#include "battle.hpp"
+#include "battle_conf.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "magic-stmt.hpp"
#include "mob.hpp"
#include "pc.hpp"
@@ -58,7 +60,10 @@
namespace tmwa
{
-struct skill_name_db skill_names[] =
+namespace map
+{
+static
+skill_name_db skill_names[] =
{
{SkillID::AC_OWL, "OWL"_s, "Owl's_Eye"_s},
@@ -90,9 +95,6 @@ struct skill_name_db skill_names[] =
{SkillID::ZERO, ""_s, ""_s}
};
-earray<skill_db_, SkillID, SkillID::MAX_SKILL_DB> skill_db;
-
-
static
int skill_attack(BF attack_type, dumb_ptr<block_list> src,
dumb_ptr<block_list> dsrc, dumb_ptr<block_list> bl,
@@ -286,7 +288,7 @@ int skill_attack(BF attack_type, dumb_ptr<block_list> src,
lv = flag.level;
dmg = battle_calc_attack(attack_type, src, bl, skillid, skilllv, flag.lo); //ダメージ計算
- damage = dmg.damage + dmg.damage2;
+ damage = dmg.damage;
if (lv == 15)
lv = -1;
@@ -347,26 +349,16 @@ int skill_attack(BF attack_type, dumb_ptr<block_list> src,
{
hp += (dmg.damage * sd->hp_drain_per) / 100;
}
- if (sd->hp_drain_rate_ && dmg.damage2 > 0
- && random_::chance({sd->hp_drain_rate_, 100}))
- {
- hp += (dmg.damage2 * sd->hp_drain_per_) / 100;
- }
if (sd->sp_drain_rate > 0 && dmg.damage > 0
&& random_::chance({sd->sp_drain_rate, 100}))
{
sp += (dmg.damage * sd->sp_drain_per) / 100;
}
- if (sd->sp_drain_rate_ > 0 && dmg.damage2 > 0
- && random_::chance({sd->sp_drain_rate_, 100}))
- {
- sp += (dmg.damage2 * sd->sp_drain_per_) / 100;
- }
if (hp || sp)
pc_heal(sd, hp, sp);
}
- return (dmg.damage + dmg.damage2); /* 与ダメを返す */
+ return (dmg.damage); /* 与ダメを返す */
}
typedef int(*SkillFunc)(dumb_ptr<block_list>, dumb_ptr<block_list>,
@@ -388,17 +380,6 @@ void skill_area_sub(dumb_ptr<block_list> bl,
}
-/* 範囲スキル使用処理小分けここまで
- * -------------------------------------------------------------------------
- */
-
-// these variables are set in the 'else' branches,
-// and used in the (recursive) 'if' branch
-// TODO kill it, kill it with fire.
-static BlockId skill_area_temp_id;
-static int skill_area_temp_hp;
-
-
/*==========================================
* スキル使用(詠唱完了、ID指定攻撃系)
* (スパゲッティに向けて1歩前進!(ダメポ))
@@ -712,8 +693,7 @@ void skill_status_change_end(dumb_ptr<block_list> bl, StatusChange type, TimerDa
{
eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
int opt_flag = 0, calc_flag = 0;
- short *sc_count;
- Option *option;
+ Opt0 *option;
Opt1 *opt1;
Opt2 *opt2;
Opt3 *opt3;
@@ -728,8 +708,6 @@ void skill_status_change_end(dumb_ptr<block_list> bl, StatusChange type, TimerDa
sc_data = battle_get_sc_data(bl);
if (not sc_data)
return;
- sc_count = battle_get_sc_count(bl);
- nullpo_retv(sc_count);
option = battle_get_option(bl);
nullpo_retv(option);
opt1 = battle_get_opt1(bl);
@@ -753,8 +731,6 @@ void skill_status_change_end(dumb_ptr<block_list> bl, StatusChange type, TimerDa
// whether we are the timer or a cancel no longer matters
assert (!sc_data[type].timer);
- assert ((*sc_count) > 0);
- (*sc_count)--;
switch (type)
{ /* 異常の種類ごとの処理 */
@@ -835,7 +811,6 @@ void skill_status_change_timer(TimerData *tid, tick_t tick, BlockId id, StatusCh
dumb_ptr<block_list> bl;
dumb_ptr<map_session_data> sd = nullptr;
eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
- //short *sc_count; //使ってない?
if ((bl = map_id2bl(id)) == nullptr)
return;
@@ -847,8 +822,6 @@ void skill_status_change_timer(TimerData *tid, tick_t tick, BlockId id, StatusCh
if (bl->bl_type == BL::PC)
sd = bl->is_player();
- //sc_count=battle_get_sc_count(bl); //使ってない?
-
if (sc_data[type].spell_invocation)
{ // Must report termination
magic::spell_effect_report_termination(sc_data[type].spell_invocation,
@@ -936,8 +909,7 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type,
{
dumb_ptr<map_session_data> sd = nullptr;
eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
- short *sc_count;
- Option *option;
+ Opt0 *option;
Opt1 *opt1;
Opt2 *opt2;
Opt3 *opt3;
@@ -949,8 +921,6 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type,
sc_data = battle_get_sc_data(bl);
if (not sc_data)
return 0;
- sc_count = battle_get_sc_count(bl);
- nullpo_retz(sc_count);
option = battle_get_option(bl);
nullpo_retz(option);
opt1 = battle_get_opt1(bl);
@@ -994,7 +964,6 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type,
/* 継ぎ足しができない状態異常である時は状態異常を行わない */
{
- (*sc_count)--;
sc_data[type].timer.cancel();
}
}
@@ -1080,8 +1049,6 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type,
if (opt_flag) /* optionの変更 */
clif_changeoption(bl);
- (*sc_count)++; /* ステータス異常の数 */
-
sc_data[type].val1 = val1;
if (sc_data[type].spell_invocation) // Supplant by newer spell
magic::spell_effect_report_termination(sc_data[type].spell_invocation,
@@ -1110,8 +1077,7 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type,
int skill_status_change_clear(dumb_ptr<block_list> bl, int type)
{
eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
- short *sc_count;
- Option *option;
+ Opt0 *option;
Opt1 *opt1;
Opt2 *opt2;
Opt3 *opt3;
@@ -1120,8 +1086,6 @@ int skill_status_change_clear(dumb_ptr<block_list> bl, int type)
sc_data = battle_get_sc_data(bl);
if (not sc_data)
return 0;
- sc_count = battle_get_sc_count(bl);
- nullpo_retz(sc_count);
option = battle_get_option(bl);
nullpo_retz(option);
opt1 = battle_get_opt1(bl);
@@ -1131,18 +1095,15 @@ int skill_status_change_clear(dumb_ptr<block_list> bl, int type)
opt3 = battle_get_opt3(bl);
nullpo_retz(opt3);
- if (*sc_count == 0)
- return 0;
for (StatusChange i : erange(StatusChange(), StatusChange::MAX_STATUSCHANGE))
{
if (sc_data[i].timer)
skill_status_change_end(bl, i, nullptr);
}
- *sc_count = 0;
*opt1 = Opt1::ZERO;
*opt2 = Opt2::ZERO;
*opt3 = Opt3::ZERO;
- *option = Option::ZERO;
+ *option = Opt0::ZERO;
if (type == 0 || type & 2)
clif_changeoption(bl);
@@ -1319,4 +1280,5 @@ skill_name_db& skill_lookup_by_name(XString name)
return ner;
return skill_names[num_names - 1];
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/skill.hpp b/src/map/skill.hpp
index ec353ce..23881d4 100644
--- a/src/map/skill.hpp
+++ b/src/map/skill.hpp
@@ -20,16 +20,15 @@
// 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 "../mmo/skill.t.hpp"
+
#include "fwd.hpp"
-#include "skill.t.hpp"
#include "skill-pools.hpp"
-#include "../strings/fwd.hpp"
#include "../strings/rstring.hpp"
#include "../strings/literal.hpp"
-#include "../generic/fwd.hpp"
#include "../generic/array.hpp"
#include "map.hpp"
@@ -37,6 +36,8 @@
namespace tmwa
{
+namespace map
+{
constexpr int MAX_SKILL_PRODUCE_DB = 150;
constexpr int MAX_SKILL_ARROW_DB = 150;
constexpr int MAX_SKILL_ABRA_DB = 350;
@@ -60,8 +61,6 @@ struct skill_db_
int weapon;
Array<int, MAX_SKILL_LEVEL> castnodex;
};
-extern
-earray<skill_db_, SkillID, SkillID::MAX_SKILL_DB> skill_db;
struct skill_name_db
{
@@ -75,9 +74,6 @@ struct skill_name_db
{}
};
-// used only by @skillid for iteration - should be depublicized
-extern struct skill_name_db skill_names[];
-
skill_name_db& skill_lookup_by_id(SkillID id);
skill_name_db& skill_lookup_by_name(XString name);
@@ -135,11 +131,6 @@ void skill_reload(void);
// Max. # of active entries in the skill pool
constexpr int MAX_SKILL_POOL = 3;
-// Max. # of skills that may be classified as pool skills in db/skill_db.txt
-constexpr int MAX_POOL_SKILLS = 128;
-
-extern Array<SkillID, MAX_POOL_SKILLS> skill_pool_skills; // All pool skills
-extern int skill_pool_skills_size; // Number of entries in skill_pool_skills
// Yields all active skills in the skill pool; no more than MAX_SKILL_POOL. Return is number of skills.
int skill_pool(dumb_ptr<map_session_data> sd, SkillID *skills);
@@ -166,4 +157,5 @@ int skill_power_bl(dumb_ptr<block_list> bl, SkillID skill);
// [Fate] Remember that a certain skill ID belongs to a pool skill
void skill_pool_register(SkillID id);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/storage.cpp b/src/map/storage.cpp
index a6e6a0d..1327146 100644
--- a/src/map/storage.cpp
+++ b/src/map/storage.cpp
@@ -25,10 +25,11 @@
#include "../generic/db.hpp"
#include "../mmo/ids.hpp"
-#include "../mmo/mmo.hpp"
+#include "../high/mmo.hpp"
#include "chrif.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "intif.hpp"
#include "itemdb.hpp"
#include "map.hpp"
@@ -39,27 +40,22 @@
namespace tmwa
{
-static
-Map<AccountId, Storage> storage_db;
-
+namespace map
+{
void do_final_storage(void)
{
storage_db.clear();
}
-Storage *account2storage(AccountId account_id)
+Borrowed<Storage> account2storage(AccountId account_id)
{
- Storage *stor = storage_db.search(account_id);
- if (stor == nullptr)
- {
- stor = storage_db.init(account_id);
- stor->account_id = account_id;
- }
+ P<Storage> stor = storage_db.init(account_id);
+ stor->account_id = account_id;
return stor;
}
// Just to ask storage, without creation
-Storage *account2storage2(AccountId account_id)
+Option<Borrowed<Storage>> account2storage2(AccountId account_id)
{
return storage_db.search(account_id);
}
@@ -81,12 +77,11 @@ int storage_storageopen(dumb_ptr<map_session_data> sd)
if (sd->state.storage_open)
return 1; //Already open?
- Storage *stor = storage_db.search(sd->status_key.account_id);
- if (stor == nullptr)
+ P<Storage> stor = TRY_UNWRAP(storage_db.search(sd->status_key.account_id),
{ //Request storage.
intif_request_storage(sd->status_key.account_id);
return 1;
- }
+ });
if (stor->storage_status)
return 1; //Already open/player already has it open...
@@ -104,15 +99,13 @@ int storage_storageopen(dumb_ptr<map_session_data> sd)
*------------------------------------------
*/
static
-int storage_additem(dumb_ptr<map_session_data> sd, Storage *stor,
+int storage_additem(dumb_ptr<map_session_data> sd, P<Storage> stor,
Item *item_data, int amount)
{
- struct item_data *data;
-
if (!item_data->nameid || amount <= 0)
return 1;
- data = itemdb_search(item_data->nameid);
+ P<struct item_data> data = itemdb_search(item_data->nameid);
if (!itemdb_isequip2(data))
{ //Stackable
@@ -152,7 +145,7 @@ int storage_additem(dumb_ptr<map_session_data> sd, Storage *stor,
*------------------------------------------
*/
static
-int storage_delitem(dumb_ptr<map_session_data> sd, Storage *stor,
+int storage_delitem(dumb_ptr<map_session_data> sd, P<Storage> stor,
SOff0 n, int amount)
{
@@ -178,11 +171,8 @@ int storage_delitem(dumb_ptr<map_session_data> sd, Storage *stor,
*/
int storage_storageadd(dumb_ptr<map_session_data> sd, IOff0 index, int amount)
{
- Storage *stor;
-
nullpo_retz(sd);
- stor = account2storage2(sd->status_key.account_id);
- nullpo_retz(stor);
+ P<Storage> stor = TRY_UNWRAP(account2storage2(sd->status_key.account_id), return 0);
if ((stor->storage_amount > MAX_STORAGE) || !stor->storage_status)
return 0; // storage full / storage closed
@@ -213,12 +203,10 @@ int storage_storageadd(dumb_ptr<map_session_data> sd, IOff0 index, int amount)
*/
int storage_storageget(dumb_ptr<map_session_data> sd, SOff0 index, int amount)
{
- Storage *stor;
PickupFail flag;
nullpo_retz(sd);
- stor = account2storage2(sd->status_key.account_id);
- nullpo_retz(stor);
+ P<Storage> stor = TRY_UNWRAP(account2storage2(sd->status_key.account_id), return 0);
if (!index.ok())
return 0;
@@ -243,11 +231,8 @@ int storage_storageget(dumb_ptr<map_session_data> sd, SOff0 index, int amount)
*/
int storage_storageclose(dumb_ptr<map_session_data> sd)
{
- Storage *stor;
-
nullpo_retz(sd);
- stor = account2storage2(sd->status_key.account_id);
- nullpo_retz(stor);
+ P<Storage> stor = TRY_UNWRAP(account2storage2(sd->status_key.account_id), return 0);
clif_storageclose(sd);
if (stor->storage_status)
@@ -275,12 +260,9 @@ int storage_storageclose(dumb_ptr<map_session_data> sd)
*/
int storage_storage_quit(dumb_ptr<map_session_data> sd)
{
- Storage *stor;
-
nullpo_retz(sd);
- stor = account2storage2(sd->status_key.account_id);
- if (stor)
+ P<Storage> stor = TRY_UNWRAP(account2storage2(sd->status_key.account_id), return 0);
{
chrif_save(sd); //Invokes the storage saving as well.
stor->storage_status = 0;
@@ -292,11 +274,7 @@ int storage_storage_quit(dumb_ptr<map_session_data> sd)
int storage_storage_save(AccountId account_id, int final)
{
- Storage *stor;
-
- stor = account2storage2(account_id);
- if (!stor)
- return 0;
+ P<Storage> stor = TRY_UNWRAP(account2storage2(account_id), return 0);
if (stor->dirty)
{
@@ -320,9 +298,8 @@ int storage_storage_save(AccountId account_id, int final)
//Ack from Char-server indicating the storage was saved. [Skotlex]
int storage_storage_saved(AccountId account_id)
{
- Storage *stor = account2storage2(account_id);
+ P<Storage> stor = TRY_UNWRAP(account2storage2(account_id), return 0);
- if (stor)
{
//Only mark it clean if it's not in use. [Skotlex]
if (stor->dirty && stor->storage_status == 0)
@@ -334,4 +311,5 @@ int storage_storage_saved(AccountId account_id)
}
return 0;
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/storage.hpp b/src/map/storage.hpp
index f3875c8..5f118c3 100644
--- a/src/map/storage.hpp
+++ b/src/map/storage.hpp
@@ -22,23 +22,24 @@
#include "fwd.hpp"
-#include "../generic/fwd.hpp"
+#include "../proto2/net-Storage.hpp"
-#include "../mmo/fwd.hpp"
-
-#include "clif.t.hpp"
+#include "../mmo/clif.t.hpp"
namespace tmwa
{
+namespace map
+{
int storage_storageopen(dumb_ptr<map_session_data> sd);
int storage_storageadd(dumb_ptr<map_session_data> sd, IOff0 index, int amount);
int storage_storageget(dumb_ptr<map_session_data> sd, SOff0 index, int amount);
int storage_storageclose(dumb_ptr<map_session_data> sd);
void do_final_storage(void);
-Storage *account2storage(AccountId account_id);
-Storage *account2storage2(AccountId account_id);
+Borrowed<Storage> account2storage(AccountId account_id);
+Option<Borrowed<Storage>> account2storage2(AccountId account_id);
int storage_storage_quit(dumb_ptr<map_session_data> sd);
int storage_storage_save(AccountId account_id, int final);
int storage_storage_saved(AccountId account_id);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/tmw.cpp b/src/map/tmw.cpp
index 60b5027..df76720 100644
--- a/src/map/tmw.cpp
+++ b/src/map/tmw.cpp
@@ -28,13 +28,16 @@
#include "../io/cxxstdio.hpp"
+#include "../net/timer.hpp"
+
#include "../mmo/human_time_diff.hpp"
-#include "../mmo/utils.hpp"
#include "atcommand.hpp"
#include "battle.hpp"
+#include "battle_conf.hpp"
#include "chrif.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "intif.hpp"
#include "map.hpp"
#include "pc.hpp"
@@ -44,8 +47,10 @@
namespace tmwa
{
+namespace map
+{
static
-void tmw_AutoBan(dumb_ptr<map_session_data> sd, ZString reason, int length);
+void tmw_AutoBan(dumb_ptr<map_session_data> sd, ZString reason, std::chrono::hours length);
static
bool tmw_CheckChatLameness(dumb_ptr<map_session_data> sd, XString message);
@@ -53,21 +58,21 @@ bool tmw_CheckChatLameness(dumb_ptr<map_session_data> sd, XString message);
int tmw_CheckChatSpam(dumb_ptr<map_session_data> sd, XString message)
{
nullpo_retr(1, sd);
- TimeT now = TimeT::now();
+ tick_t now = gettick();
if (pc_isGM(sd))
return 0;
if (now > sd->chat_reset_due)
{
- sd->chat_reset_due = static_cast<time_t>(now) + battle_config.chat_spam_threshold;
+ sd->chat_reset_due = now + battle_config.chat_spam_threshold;
sd->chat_lines_in = 0;
}
if (now > sd->chat_repeat_reset_due)
{
sd->chat_repeat_reset_due =
- static_cast<time_t>(now) + (battle_config.chat_spam_threshold * 60);
+ now + (battle_config.chat_spam_threshold * 60);
sd->chat_total_repeats = 0;
}
@@ -100,20 +105,20 @@ int tmw_CheckChatSpam(dumb_ptr<map_session_data> sd, XString message)
return 1;
}
- if (battle_config.chat_spam_ban &&
+ if (battle_config.chat_spam_ban != std::chrono::hours::zero() &&
(sd->chat_lines_in >= battle_config.chat_spam_warn
|| sd->chat_total_repeats >= battle_config.chat_spam_warn))
{
- clif_displaymessage(sd->sess, "WARNING: You are about to be automatically banned for spam!"_s);
- clif_displaymessage(sd->sess, "WARNING: Please slow down, do not repeat, and do not SHOUT!"_s);
+ clif_displaymessage(sd->sess, "##1WARNING : ##BYou are about to be automatically banned for spam!"_s);
+ clif_displaymessage(sd->sess, "##1WARNING : ##BPlease slow down, do not repeat, and do not SHOUT!"_s);
}
return 0;
}
-void tmw_AutoBan(dumb_ptr<map_session_data> sd, ZString reason, int length)
+void tmw_AutoBan(dumb_ptr<map_session_data> sd, ZString reason, std::chrono::hours length)
{
- if (length == 0 || sd->auto_ban_info.in_progress)
+ if (length == std::chrono::hours::zero() || sd->auto_ban_info.in_progress)
return;
sd->auto_ban_info.in_progress = 1;
@@ -124,7 +129,7 @@ void tmw_AutoBan(dumb_ptr<map_session_data> sd, ZString reason, int length)
tmw_GmHackMsg(hack_msg);
AString fake_command = STRPRINTF("@autoban %s %dh (%s spam)"_fmt,
- sd->status_key.name, length, reason);
+ sd->status_key.name, static_cast<uint16_t>(length.count()), reason);
log_atcommand(sd, fake_command);
AString anotherbuf = STRPRINTF("You have been banned for %s spamming. Please do not spam."_fmt,
@@ -133,7 +138,7 @@ void tmw_AutoBan(dumb_ptr<map_session_data> sd, ZString reason, int length)
clif_displaymessage(sd->sess, anotherbuf);
/* type: 2 - ban(year, month, day, hour, minute, second) */
HumanTimeDiff ban_len {};
- ban_len.hour = length;
+ ban_len.hour = length.count();
chrif_char_ask_name(AccountId(), sd->status_key.name, 2, ban_len);
clif_setwaitclose(sd->sess);
}
@@ -162,8 +167,9 @@ bool tmw_CheckChatLameness(dumb_ptr<map_session_data>, XString message)
// Sends a whisper to all GMs
void tmw_GmHackMsg(ZString line)
{
- intif_wis_message_to_gm(wisp_server_name,
- GmLevel::from(static_cast<uint32_t>(battle_config.hack_info_GM_level)),
+ intif_wis_message_to_gm(WISP_SERVER_NAME,
+ battle_config.hack_info_GM_level,
line);
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/tmw.hpp b/src/map/tmw.hpp
index ffd6f7f..bc0043c 100644
--- a/src/map/tmw.hpp
+++ b/src/map/tmw.hpp
@@ -21,13 +21,12 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
-#include "../generic/fwd.hpp"
-
namespace tmwa
{
+namespace map
+{
int tmw_CheckChatSpam(dumb_ptr<map_session_data> sd, XString message);
void tmw_GmHackMsg(ZString line);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/trade.cpp b/src/map/trade.cpp
index c52377d..c03609c 100644
--- a/src/map/trade.cpp
+++ b/src/map/trade.cpp
@@ -25,7 +25,9 @@
#include "../io/cxxstdio.hpp"
#include "battle.hpp"
+#include "battle_conf.hpp"
#include "clif.hpp"
+#include "globals.hpp"
#include "itemdb.hpp"
#include "map.hpp"
#include "npc.hpp"
@@ -37,6 +39,8 @@
namespace tmwa
{
+namespace map
+{
/*==========================================
* 取引要請を相手に送る
*------------------------------------------
@@ -129,7 +133,6 @@ void trade_tradeack(dumb_ptr<map_session_data> sd, int type)
void trade_tradeadditem(dumb_ptr<map_session_data> sd, IOff2 index, int amount)
{
dumb_ptr<map_session_data> target_sd;
- struct item_data *id;
int trade_i;
int trade_weight = 0;
int free_ = 0;
@@ -156,7 +159,7 @@ void trade_tradeadditem(dumb_ptr<map_session_data> sd, IOff2 index, int amount)
for (IOff0 i : IOff0::iter())
{
if (!target_sd->status.inventory[i].nameid
- && target_sd->inventory_data[i] == nullptr)
+ && target_sd->inventory_data[i].is_none())
free_++;
}
for (trade_i = 0; trade_i < TRADE_MAX; trade_i++)
@@ -164,26 +167,31 @@ void trade_tradeadditem(dumb_ptr<map_session_data> sd, IOff2 index, int amount)
if (sd->deal_item_amount[trade_i] == 0)
{
// calculate trade weight
+ // note: 'abort' branch is protected by 'amount' check above
trade_weight +=
- sd->inventory_data[index.unshift()]->weight * amount;
+ TRY_UNWRAP(sd->inventory_data[index.unshift()], abort())->weight * amount;
// determine if item is a stackable already in receivers inventory, and up free count
for (IOff0 i : IOff0::iter())
{
- if (target_sd->status.inventory[i].nameid ==
- sd->status.inventory[index.unshift()].nameid
- && target_sd->inventory_data[i] != nullptr)
+ if (target_sd->status.inventory[i].nameid !=
+ sd->status.inventory[index.unshift()].nameid)
+ continue;
+
+ OMATCH_BEGIN_SOME (id, target_sd->inventory_data[i])
{
- id = target_sd->inventory_data[i];
if (id->type != ItemType::WEAPON
&& id->type != ItemType::ARMOR
&& id->type != ItemType::_7
&& id->type != ItemType::_8)
{
free_++;
- break;
+ goto break_outer1;
}
}
+ OMATCH_END ();
+ break_outer1:
+ break;
}
if (target_sd->weight + trade_weight >
@@ -218,28 +226,32 @@ void trade_tradeadditem(dumb_ptr<map_session_data> sd, IOff2 index, int amount)
else
{
// calculate weight for stored deal
+ // note: 'abort' branch is protected by 'amount' check above
trade_weight +=
- sd->inventory_data[sd->deal_item_index[trade_i].unshift()
- ]->weight *
+ TRY_UNWRAP(sd->inventory_data[sd->deal_item_index[trade_i].unshift()
+ ], abort())->weight *
sd->deal_item_amount[trade_i];
// count free stackables in stored deal
for (IOff0 i : IOff0::iter())
{
- if (target_sd->status.inventory[i].nameid ==
+ if (target_sd->status.inventory[i].nameid !=
sd->status.
- inventory[sd->deal_item_index[trade_i].unshift()].nameid
- && target_sd->inventory_data[i] != nullptr)
+ inventory[sd->deal_item_index[trade_i].unshift()].nameid)
+ continue;
+ OMATCH_BEGIN_SOME (id, target_sd->inventory_data[i])
{
- id = target_sd->inventory_data[i];
if (id->type != ItemType::WEAPON
&& id->type != ItemType::ARMOR
&& id->type != ItemType::_7
&& id->type != ItemType::_8)
{
free_++;
- break;
+ goto break_outer2;
}
}
+ OMATCH_END ();
+ break_outer2:
+ break;
}
}
// used a slot, but might be cancelled out by stackable checks above
@@ -465,4 +477,5 @@ void trade_verifyzeny(dumb_ptr<map_session_data> sd)
}
}
}
+} // namespace map
} // namespace tmwa
diff --git a/src/map/trade.hpp b/src/map/trade.hpp
index 91ed954..569524b 100644
--- a/src/map/trade.hpp
+++ b/src/map/trade.hpp
@@ -22,13 +22,13 @@
#include "fwd.hpp"
-#include "../generic/fwd.hpp"
-
-#include "clif.t.hpp"
+#include "../mmo/clif.t.hpp"
namespace tmwa
{
+namespace map
+{
void trade_traderequest(dumb_ptr<map_session_data> sd, BlockId target_id);
void trade_tradeack(dumb_ptr<map_session_data> sd, int type);
void trade_tradeadditem(dumb_ptr<map_session_data> sd, IOff2 index, int amount);
@@ -36,4 +36,5 @@ void trade_tradeok(dumb_ptr<map_session_data> sd);
void trade_tradecancel(dumb_ptr<map_session_data> sd);
void trade_tradecommit(dumb_ptr<map_session_data> sd);
void trade_verifyzeny(dumb_ptr<map_session_data> sd);
+} // namespace map
} // namespace tmwa
diff --git a/src/map/clif.t.hpp b/src/mmo/clif.t.hpp
index 1789ee8..0a51523 100644
--- a/src/map/clif.t.hpp
+++ b/src/mmo/clif.t.hpp
@@ -30,8 +30,8 @@
#include "../generic/enum.hpp"
-#include "../mmo/consts.hpp"
-#include "../mmo/enums.hpp"
+#include "consts.hpp"
+#include "enums.hpp"
namespace tmwa
@@ -40,7 +40,7 @@ namespace e
{
// [Fate] status.option properties. These are persistent status changes.
// IDs that are not listed are not used in the code (to the best of my knowledge)
-enum class Option : uint16_t
+enum class Opt0 : uint16_t
{
ZERO = 0x0000,
@@ -88,11 +88,11 @@ enum class Opt3 : uint16_t
_assumptio = 0x0800,
};
-ENUM_BITWISE_OPERATORS(Option)
+ENUM_BITWISE_OPERATORS(Opt0)
ENUM_BITWISE_OPERATORS(Opt2)
ENUM_BITWISE_OPERATORS(Opt3)
}
-using e::Option;
+using e::Opt0;
using e::Opt1;
using e::Opt2;
using e::Opt3;
diff --git a/src/mmo/config_parse.cpp b/src/mmo/config_parse.cpp
index 8362810..6e43471 100644
--- a/src/mmo/config_parse.cpp
+++ b/src/mmo/config_parse.cpp
@@ -23,7 +23,10 @@
#include "../strings/xstring.hpp"
#include "../strings/zstring.hpp"
+#include "../compat/borrow.hpp"
+
#include "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
#include "../io/line.hpp"
#include "version.hpp"
@@ -39,39 +42,138 @@ bool is_comment(XString line)
}
template<class ZS>
+static
+bool do_split(io::Spanned<ZS> line, io::Spanned<XString> *key, io::Spanned<ZS> *value)
+{
+ typename ZS::iterator colon = std::find(line.data.begin(), line.data.end(), ':');
+ if (colon == line.data.end())
+ return false;
+ key->data = line.data.xislice_h(colon);
+ key->span = line.span;
+ key->span.end.column = key->span.begin.column + key->data.size() - 1;
+ ++colon;
+ value->data = line.data.xislice_t(colon);
+ value->span = line.span;
+ value->span.begin.column = value->span.end.column - value->data.size() + 1;
+ return true;
+}
+
+template<class ZS>
+static
+io::Spanned<ZS> do_lstrip(io::Spanned<ZS> value)
+{
+ io::Spanned<ZS> rv;
+ rv.data = value.data.lstrip();
+ rv.span = value.span;
+ rv.span.begin.column += (value.data.size() - rv.data.size());
+ return rv;
+}
+
+static
+io::Spanned<XString> do_rstrip(io::Spanned<XString> value)
+{
+ io::Spanned<XString> rv;
+ rv.data = value.data.rstrip();
+ rv.span = value.span;
+ rv.span.end.column -= (value.data.size() - rv.data.size());
+ return rv;
+}
+
+static
+io::Spanned<XString> do_strip(io::Spanned<XString> value)
+{
+ return do_lstrip(do_rstrip(value));
+}
+
+template<class ZS>
inline
-bool config_split_impl(ZS line, XString *key, ZS *value)
+bool config_split_impl(io::Spanned<ZS> line, io::Spanned<XString> *key, io::Spanned<ZS> *value)
{
// unconditionally fail if line contains control characters
- if (std::find_if(line.begin(), line.end(),
+ if (std::find_if(line.data.begin(), line.data.end(),
[](unsigned char c) { return c < ' '; }
- ) != line.end())
- return false;
- // try to find a colon, fail if not found
- typename ZS::iterator colon = std::find(line.begin(), line.end(), ':');
- if (colon == line.end())
+ ) != line.data.end())
return false;
- *key = line.xislice_h(colon).strip();
- // move past the colon and any spaces
- ++colon;
- *value = line.xislice_t(colon).lstrip();
+ if (!do_split(line, key, value))
+ return false;
+ *key = do_strip(*key);
+ *value = do_lstrip(*value);
return true;
}
// eventually this should go away
-bool config_split(ZString line, XString *key, ZString *value)
+// currently the only real offenders are io::FD::open and *PRINTF
+bool config_split(io::Spanned<ZString> line, io::Spanned<XString> *key, io::Spanned<ZString> *value)
{
return config_split_impl(line, key, value);
}
-// and use this instead
-bool config_split(XString line, XString *key, XString *value)
+
+static
+bool check_version(io::Spanned<XString> avers, Borrowed<bool> valid)
{
- return config_split_impl(line, key, value);
+ enum
+ {
+ GE, LE, GT, LT
+ } cmp;
+
+ if (avers.data.startswith(">="_s))
+ {
+ cmp = GE;
+ avers.data = avers.data.xslice_t(2);
+ avers.span.begin.column += 2;
+ }
+ else if (avers.data.startswith('>'))
+ {
+ cmp = GT;
+ avers.data = avers.data.xslice_t(1);
+ avers.span.begin.column += 1;
+ }
+ else if (avers.data.startswith("<="_s))
+ {
+ cmp = LE;
+ avers.data = avers.data.xslice_t(2);
+ avers.span.begin.column += 2;
+ }
+ else if (avers.data.startswith('<'))
+ {
+ cmp = LT;
+ avers.data = avers.data.xslice_t(1);
+ avers.span.begin.column += 1;
+ }
+ else
+ {
+ avers.span.error("Version check must begin with one of: '>=', '>', '<=', '<'"_s);
+ *valid = false;
+ return false;
+ }
+
+ Version vers;
+ if (!extract(avers.data, &vers))
+ {
+ avers.span.error("Bad value"_s);
+ *valid = false;
+ return false;
+ }
+ switch (cmp)
+ {
+ case GE:
+ return CURRENT_VERSION >= vers;
+ case GT:
+ return CURRENT_VERSION > vers;
+ case LE:
+ return CURRENT_VERSION <= vers;
+ case LT:
+ return CURRENT_VERSION < vers;
+ }
+ abort();
}
/// Master config parser. This handles 'import' and 'version-ge' etc.
/// Then it defers to the inferior parser for a line it does not understand.
+///
+/// Note: old-style 'version-ge: 1.2.3' etc apply to the rest of the file, but
+/// new-style 'version: >= 1.2.3' apply only up to the next 'version:'
bool load_config_file(ZString filename, ConfigItemParser slave)
{
io::LineReader in(filename);
@@ -80,30 +182,66 @@ bool load_config_file(ZString filename, ConfigItemParser slave)
PRINTF("Unable to open file: %s\n"_fmt, filename);
return false;
}
- io::Line line;
+ io::Line line_;
bool rv = true;
- while (in.read_line(line))
+ bool good_version = true;
+ while (in.read_line(line_))
{
- if (is_comment(line.text))
+ if (is_comment(line_.text))
continue;
- XString key;
- ZString value;
- if (!config_split(line.text, &key, &value))
+ auto line = io::respan(line_.to_span(), ZString(line_.text));
+ io::Spanned<XString> key;
+ io::Spanned<ZString> value;
+ if (!config_split(line, &key, &value))
{
- line.error("Bad config line"_s);
+ line.span.error("Bad config line"_s);
rv = false;
continue;
}
- if (key == "import"_s)
+ if (key.data == "version"_s)
+ {
+ if (value.data == "all"_s)
+ {
+ good_version = true;
+ }
+ else
+ {
+ good_version = true;
+ while (good_version && value.data)
+ {
+ ZString::iterator it = std::find(value.data.begin(), value.data.end(), ' ');
+ io::Spanned<XString> value_head;
+ value_head.data = value.data.xislice_h(it);
+ value_head.span = value.span;
+ value.data = value.data.xislice_t(it).lstrip();
+
+ value_head.span.end.column = value_head.span.begin.column + value_head.data.size() - 1;
+ value.span.begin.column = value.span.end.column - value.data.size() + 1;
+
+ good_version &= check_version(value_head, borrow(rv));
+ }
+ }
+ continue;
+ }
+ if (!good_version)
{
- rv &= load_config_file(value, slave);
continue;
}
- else if (key == "version-lt"_s)
+ if (key.data == "import"_s)
+ {
+ if (!load_config_file(value.data, slave))
+ {
+ value.span.error("Failed to include file"_s);
+ rv = false;
+ }
+ continue;
+ }
+ else if (key.data == "version-lt"_s)
{
Version vers;
- if (!extract(value, &vers))
+ if (!extract(value.data, &vers))
{
+ value.span.error("Bad value"_s);
rv = false;
continue;
}
@@ -111,47 +249,48 @@ bool load_config_file(ZString filename, ConfigItemParser slave)
continue;
break;
}
- else if (key == "version-le"_s)
+ else if (key.data == "version-le"_s)
{
Version vers;
- if (!extract(value, &vers))
+ if (!extract(value.data, &vers))
{
rv = false;
+ value.span.error("Bad value"_s);
continue;
}
if (CURRENT_VERSION <= vers)
continue;
break;
}
- else if (key == "version-gt"_s)
+ else if (key.data == "version-gt"_s)
{
Version vers;
- if (!extract(value, &vers))
+ if (!extract(value.data, &vers))
{
rv = false;
+ value.span.error("Bad value"_s);
continue;
}
if (CURRENT_VERSION > vers)
continue;
break;
}
- else if (key == "version-ge"_s)
+ else if (key.data == "version-ge"_s)
{
Version vers;
- if (!extract(value, &vers))
+ if (!extract(value.data, &vers))
{
rv = false;
+ value.span.error("Bad value"_s);
continue;
}
if (CURRENT_VERSION >= vers)
continue;
break;
}
- else if (!slave(key, value))
+ else
{
- line.error("Bad config key or value"_s);
- rv = false;
- continue;
+ rv &= slave(key, value);
}
// nothing to see here, move along
}
diff --git a/src/mmo/config_parse.hpp b/src/mmo/config_parse.hpp
index 50a115e..06432ba 100644
--- a/src/mmo/config_parse.hpp
+++ b/src/mmo/config_parse.hpp
@@ -20,16 +20,13 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
namespace tmwa
{
-typedef bool (*ConfigItemParser)(XString key, ZString value);
+using ConfigItemParser = bool(io::Spanned<XString> key, io::Spanned<ZString> value);
bool is_comment(XString line);
-bool config_split(ZString line, XString *key, ZString *value);
-bool config_split(XString line, XString *key, XString *value);
+bool config_split(io::Spanned<ZString> line, io::Spanned<XString> *key, io::Spanned<ZString> *value);
/// Master config parser. This handles 'import' and 'version-ge' etc.
/// Then it defers to the inferior parser for a line it does not understand.
diff --git a/src/mmo/config_parse_test.cpp b/src/mmo/config_parse_test.cpp
new file mode 100644
index 0000000..e1170cb
--- /dev/null
+++ b/src/mmo/config_parse_test.cpp
@@ -0,0 +1,60 @@
+#include "config_parse.hpp"
+// config_parse_test.cpp - Testsuite for config parsers
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 <gtest/gtest.h>
+
+#include "../strings/literal.hpp"
+#include "../strings/rstring.hpp"
+
+#include "../io/span.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+#define EXPECT_SPAN(span, bl,bc, el,ec) \
+ ({ \
+ EXPECT_EQ((span).begin.line, bl); \
+ EXPECT_EQ((span).begin.column, bc); \
+ EXPECT_EQ((span).end.line, el); \
+ EXPECT_EQ((span).end.column, ec); \
+ })
+TEST(configparse, keyvalue)
+{
+ // 0 1 2 3
+ // 123456789012345678901234567890
+ RString data = " key : value "_s;
+
+ io::Spanned<ZString> input, value;
+ io::Spanned<XString> key;
+ input.data = data;
+ input.span.begin.text = data;
+ input.span.begin.filename = "<config parse key/value test>"_s;
+ input.span.begin.line = 1;
+ input.span.begin.column = 1;
+ input.span.end = input.span.begin;
+ input.span.end.column = data.size();
+ EXPECT_EQ(data.size(), 30);
+ ASSERT_TRUE(config_split(input, &key, &value));
+ EXPECT_SPAN(key.span, 1,3, 1,5);
+ EXPECT_SPAN(value.span, 1,18, 1,30);
+}
+} // namespace tmwa
diff --git a/src/mmo/consts.hpp b/src/mmo/consts.hpp
index c1a7eb6..5533446 100644
--- a/src/mmo/consts.hpp
+++ b/src/mmo/consts.hpp
@@ -54,6 +54,11 @@ constexpr int MAX_PARTY = 12;
#define MIN_CLOTH_COLOR battle_config.min_cloth_color
#define MAX_CLOTH_COLOR battle_config.max_cloth_color
+namespace map
+{
+ struct map_session_data;
+}
+
// WTF is this doing here?
struct PartyMember
{
@@ -61,6 +66,6 @@ struct PartyMember
CharName name;
MapName map;
int leader, online, lv;
- struct map_session_data *sd;
+ map::map_session_data *sd;
};
} // namespace tmwa
diff --git a/src/io/cxxstdio_enums.hpp b/src/mmo/cxxstdio_enums.hpp
index 05cdcae..28a8a14 100644
--- a/src/io/cxxstdio_enums.hpp
+++ b/src/mmo/cxxstdio_enums.hpp
@@ -29,49 +29,58 @@ namespace tmwa
{
namespace e
{
-enum class BF : uint16_t;
enum class EPOS : uint16_t;
-enum class MapCell : uint8_t;
-enum class Option : uint16_t;
+enum class Opt0 : uint16_t;
inline
-auto decay_for_printf(BF v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
auto decay_for_printf(EPOS v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
inline
-auto decay_for_printf(MapCell v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+auto decay_for_printf(Opt0 v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+} // namespace e
+
+enum class ItemLook : uint16_t;
+enum class SP : uint16_t;
+enum class SkillID : uint16_t;
+enum class StatusChange : uint16_t;
+
+inline
+auto decay_for_printf(ItemLook v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+inline
+auto decay_for_printf(SP v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
inline
-auto decay_for_printf(Option v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-}
+auto decay_for_printf(SkillID v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+inline
+auto decay_for_printf(StatusChange v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+
+namespace map
+{
+namespace e
+{
+enum class BF : uint16_t;
+enum class MapCell : uint8_t;
+inline
+auto decay_for_printf(BF v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+inline
+auto decay_for_printf(MapCell v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+} // namespace map::e
namespace magic
{
enum class SPELLARG : uint8_t;
inline
auto decay_for_printf(SPELLARG v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-}
+} // namespace map::magic
enum class BL : uint8_t;
enum class ByteCode : uint8_t;
-enum class ItemLook : uint16_t;
enum class MS : uint8_t;
-enum class SP : uint16_t;
-enum class SkillID : uint16_t;
-enum class StatusChange : uint16_t;
inline
auto decay_for_printf(BL v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
inline
auto decay_for_printf(ByteCode v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
inline
-auto decay_for_printf(ItemLook v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
auto decay_for_printf(MS v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(SP v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(SkillID v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(StatusChange v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+} // namespace map
} // namespace tmwa
diff --git a/src/mmo/enums.cpp b/src/mmo/enums.cpp
deleted file mode 100644
index d05be91..0000000
--- a/src/mmo/enums.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "enums.hpp"
-// enums.cpp - Common enumerated types
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/mmo/enums.hpp b/src/mmo/enums.hpp
index bf8a75c..caecc13 100644
--- a/src/mmo/enums.hpp
+++ b/src/mmo/enums.hpp
@@ -66,14 +66,14 @@ enum class SkillFlags : uint16_t;
}
using e::SkillFlags;
-// Option and Opt1..3 in map.hpp
+// Opt0 and Opt1..3 in map.hpp
namespace e
{
-enum class Option : uint16_t;
-constexpr Option get_enum_min_value(Option) { return Option(0x0000); }
-constexpr Option get_enum_max_value(Option) { return Option(0xffff); }
+enum class Opt0 : uint16_t;
+constexpr Opt0 get_enum_min_value(Opt0) { return Opt0(0x0000); }
+constexpr Opt0 get_enum_max_value(Opt0) { return Opt0(0xffff); }
}
-using e::Option;
+using e::Opt0;
enum class ATTR
{
@@ -101,22 +101,10 @@ enum class ItemLook : uint16_t
{
NONE = 0,
BLADE = 1, // or some other common weapons
- _2,
SETZER_AND_SCYTHE = 3,
- _6,
STAFF = 10,
BOW = 11,
- _13 = 13,
- _14 = 14,
- _16 = 16,
- SINGLE_HANDED_COUNT = 17,
-
- DUAL_BLADE = 0x11,
- DUAL_2 = 0x12,
- DUAL_6 = 0x13,
- DUAL_12 = 0x14,
- DUAL_16 = 0x15,
- DUAL_26 = 0x16,
+ COUNT = 17,
};
enum class SEX : uint8_t
@@ -124,6 +112,7 @@ enum class SEX : uint8_t
FEMALE = 0,
MALE = 1,
// For items. This is also used as error, sometime.
+ // TODO switch to Option<SEX> where appropriate.
NEUTRAL = 2,
};
inline
diff --git a/src/mmo/extract_enums.cpp b/src/mmo/extract_enums.cpp
index f906179..efafa39 100644
--- a/src/mmo/extract_enums.cpp
+++ b/src/mmo/extract_enums.cpp
@@ -23,4 +23,37 @@
namespace tmwa
{
+bool impl_extract(XString str, DIR *d)
+{
+ unsigned di;
+ if (extract(str, &di) && di < 8)
+ {
+ *d = static_cast<DIR>(di);
+ return true;
+ }
+ const struct
+ {
+ LString str;
+ DIR d;
+ } dirs[] =
+ {
+ {"S"_s, DIR::S},
+ {"SW"_s, DIR::SW},
+ {"W"_s, DIR::W},
+ {"NW"_s, DIR::NW},
+ {"N"_s, DIR::N},
+ {"NE"_s, DIR::NE},
+ {"E"_s, DIR::E},
+ {"SE"_s, DIR::SE},
+ };
+ for (auto& pair : dirs)
+ {
+ if (str == pair.str)
+ {
+ *d = pair.d;
+ return true;
+ }
+ }
+ return false;
+}
} // namespace tmwa
diff --git a/src/mmo/extract_enums.hpp b/src/mmo/extract_enums.hpp
index 613fae9..0e8ac4c 100644
--- a/src/mmo/extract_enums.hpp
+++ b/src/mmo/extract_enums.hpp
@@ -22,7 +22,9 @@
#include <cstdint>
-#include "extract.hpp"
+#include "../io/extract.hpp"
+
+#include "clif.t.hpp"
namespace tmwa
@@ -30,40 +32,54 @@ namespace tmwa
namespace e
{
enum class EPOS : uint16_t;
-enum class MobMode : uint16_t;
enum class Opt1 : uint16_t;
enum class Opt2 : uint16_t;
-enum class Option : uint16_t;
+enum class Opt0 : uint16_t;
inline
-bool extract(XString str, EPOS *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, EPOS *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, MobMode *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, Opt1 *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, Opt1 *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, Opt2 *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, Opt2 *iv) { return extract_as_int(str, iv); }
-inline
-bool extract(XString str, Option *iv) { return extract_as_int(str, iv); }
-}
+bool impl_extract(XString str, Opt0 *iv) { return extract_as_int(str, iv); }
+} // namespace e
enum class ItemLook : uint16_t;
enum class ItemType : uint8_t;
-enum class Race : uint8_t;
enum class SEX : uint8_t;
enum class SkillID : uint16_t;
enum class StatusChange : uint16_t;
inline
-bool extract(XString str, ItemLook *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, ItemLook *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, ItemType *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, ItemType *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, Race *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, SEX *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, SEX *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, SkillID *iv) { return extract_as_int(str, iv); }
+inline
+bool impl_extract(XString str, StatusChange *iv) { return extract_as_int(str, iv); }
+
+bool impl_extract(XString, DIR *);
+
+namespace map
+{
+namespace e
+{
+enum class MobMode : uint16_t;
+
+inline
+bool impl_extract(XString str, MobMode *iv) { return extract_as_int(str, iv); }
+} // namespace map::e
+enum class Race : uint8_t;
+enum class ATK;
+
inline
-bool extract(XString str, SkillID *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, Race *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, StatusChange *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, ATK *iv) { return extract_as_int(str, iv); }
+} // namespace map
} // namespace tmwa
diff --git a/src/mmo/fwd.hpp b/src/mmo/fwd.hpp
index 3b56bfb..434885e 100644
--- a/src/mmo/fwd.hpp
+++ b/src/mmo/fwd.hpp
@@ -20,21 +20,29 @@
#include "../sanity.hpp"
+#include "../ints/fwd.hpp" // rank 1
+#include "../strings/fwd.hpp" // rank 1
+#include "../compat/fwd.hpp" // rank 2
+#include "../generic/fwd.hpp" // rank 3
+#include "../io/fwd.hpp" // rank 4
+#include "../net/fwd.hpp" // rank 5
+// mmo/fwd.hpp is rank 6
+
namespace tmwa
{
// meh, add more when I feel like it
class MapName;
class CharName;
-class CharPair;
class HumanTimeDiff;
+class Species;
class AccountId;
class CharId;
class PartyId;
-class ItemUnkId;
class ItemNameId;
+class BlockId;
class GmLevel;
class AccountName;
@@ -43,26 +51,14 @@ class AccountCrypt;
class AccountEmail;
class ServerName;
class PartyName;
+class QuestId;
class VarName;
class MapName;
class CharName;
-class Item;
-#if 0
-class Point;
-class SkillValue;
-#endif
-class GlobalReg;
-#if 0
-class CharKey;
-class CharData;
-class CharPair;
-#endif
-class Storage;
-#if 0
-class GM_Account;
-class PartyMember;
-#endif
-class PartyMost;
-class PartyPair;
+struct MobName;
+struct NpcName;
+struct ScriptLabel;
+struct ItemName;
+struct NpcEvent;
} // namespace tmwa
diff --git a/src/mmo/human_time_diff.cpp b/src/mmo/human_time_diff.cpp
index 49a7664..aaa7928 100644
--- a/src/mmo/human_time_diff.cpp
+++ b/src/mmo/human_time_diff.cpp
@@ -18,9 +18,51 @@
// 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 "../io/extract.hpp"
+
#include "../poison.hpp"
namespace tmwa
{
+bool impl_extract(XString str, HumanTimeDiff *iv)
+{
+ // str is a sequence of [-+]?[0-9]+([ay]|m|[jd]|h|mn|s)
+ // there are NO spaces here
+ // parse by counting the number starts
+ auto is_num = [](char c)
+ { return c == '-' || c == '+' || ('0' <= c && c <= '9'); };
+ if (!str || !is_num(str.front()))
+ return false;
+ *iv = HumanTimeDiff{};
+ while (str)
+ {
+ auto it = std::find_if_not(str.begin(), str.end(), is_num);
+ auto it2 = std::find_if(it, str.end(), is_num);
+ XString number = str.xislice_h(it);
+ XString suffix = str.xislice(it, it2);
+ str = str.xislice_t(it2);
+
+ short *ptr = nullptr;
+ if (suffix == "y"_s || suffix == "a"_s)
+ ptr = &iv->year;
+ else if (suffix == "m"_s)
+ ptr = &iv->month;
+ else if (suffix == "j"_s || suffix == "d"_s)
+ ptr = &iv->day;
+ else if (suffix == "h"_s)
+ ptr = &iv->hour;
+ else if (suffix == "mn"_s)
+ ptr = &iv->minute;
+ else if (suffix == "s"_s)
+ ptr = &iv->second;
+ else
+ return false;
+ if (number.startswith('+') && !number.startswith("+-"_s))
+ number = number.xslice_t(1);
+ if (*ptr || !extract(number, ptr))
+ return false;
+ }
+ return true;
+}
} // namespace tmwa
diff --git a/src/mmo/human_time_diff.hpp b/src/mmo/human_time_diff.hpp
index b5c19fb..92b3288 100644
--- a/src/mmo/human_time_diff.hpp
+++ b/src/mmo/human_time_diff.hpp
@@ -24,8 +24,6 @@
#include "../strings/xstring.hpp"
-#include "extract.hpp"
-
namespace tmwa
{
@@ -44,46 +42,5 @@ struct HumanTimeDiff
return !bool(*this);
}
};
-
-inline
-bool extract(XString str, HumanTimeDiff *iv)
-{
- // str is a sequence of [-+]?[0-9]+([ay]|m|[jd]|h|mn|s)
- // there are NO spaces here
- // parse by counting the number starts
- auto is_num = [](char c)
- { return c == '-' || c == '+' || ('0' <= c && c <= '9'); };
- if (!str || !is_num(str.front()))
- return false;
- *iv = HumanTimeDiff{};
- while (str)
- {
- auto it = std::find_if_not(str.begin(), str.end(), is_num);
- auto it2 = std::find_if(it, str.end(), is_num);
- XString number = str.xislice_h(it);
- XString suffix = str.xislice(it, it2);
- str = str.xislice_t(it2);
-
- short *ptr = nullptr;
- if (suffix == "y"_s || suffix == "a"_s)
- ptr = &iv->year;
- else if (suffix == "m"_s)
- ptr = &iv->month;
- else if (suffix == "j"_s || suffix == "d"_s)
- ptr = &iv->day;
- else if (suffix == "h"_s)
- ptr = &iv->hour;
- else if (suffix == "mn"_s)
- ptr = &iv->minute;
- else if (suffix == "s"_s)
- ptr = &iv->second;
- else
- return false;
- if (number.startswith('+') && !number.startswith("+-"_s))
- number = number.xslice_t(1);
- if (*ptr || !extract(number, ptr))
- return false;
- }
- return true;
-}
+bool impl_extract(XString str, HumanTimeDiff *iv);
} // namespace tmwa
diff --git a/src/mmo/human_time_diff_test.cpp b/src/mmo/human_time_diff_test.cpp
index c18599d..08a75bf 100644
--- a/src/mmo/human_time_diff_test.cpp
+++ b/src/mmo/human_time_diff_test.cpp
@@ -20,6 +20,8 @@
#include <gtest/gtest.h>
+#include "../io/extract.hpp"
+
#include "../poison.hpp"
diff --git a/src/mmo/ids.cpp b/src/mmo/ids.cpp
index d40d5c3..65c470b 100644
--- a/src/mmo/ids.cpp
+++ b/src/mmo/ids.cpp
@@ -18,9 +18,25 @@
// 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 "../io/extract.hpp"
+
#include "../poison.hpp"
namespace tmwa
{
+bool impl_extract(XString str, Species *w)
+{
+ // lots of data files use this
+ if (str == "-1"_s)
+ {
+ *w = NEGATIVE_SPECIES;
+ return true;
+ }
+ return extract(str, &w->_value);
+}
+bool impl_extract(XString str, GmLevel *lvl)
+{
+ return extract(str, &lvl->bits);
+}
} // namespace tmwa
diff --git a/src/mmo/ids.hpp b/src/mmo/ids.hpp
index 4e2b97c..28b146a 100644
--- a/src/mmo/ids.hpp
+++ b/src/mmo/ids.hpp
@@ -23,8 +23,6 @@
#include "../ints/little.hpp"
#include "../ints/wrap.hpp"
-#include "extract.hpp"
-
namespace tmwa
{
@@ -32,17 +30,7 @@ class Species : public Wrapped<uint16_t> { public: explicit operator bool() cons
constexpr Species NEGATIVE_SPECIES = Species();
-inline
-bool extract(XString str, Species *w)
-{
- // lots of data files use this
- if (str == "-1"_s)
- {
- *w = NEGATIVE_SPECIES;
- return true;
- }
- return extract(str, &w->_value);
-}
+bool impl_extract(XString str, Species *w);
class AccountId : public Wrapped<uint32_t> { public: constexpr AccountId() : Wrapped<uint32_t>() {} protected: constexpr explicit AccountId(uint32_t a) : Wrapped<uint32_t>(a) {} };
@@ -52,12 +40,14 @@ class PartyId : public Wrapped<uint32_t> { public: constexpr PartyId() : Wrapped
class ItemNameId : public Wrapped<uint16_t> { public: constexpr ItemNameId() : Wrapped<uint16_t>() {} protected: constexpr explicit ItemNameId(uint16_t a) : Wrapped<uint16_t>(a) {} };
class BlockId : public Wrapped<uint32_t> { public: constexpr BlockId() : Wrapped<uint32_t>() {} protected: constexpr explicit BlockId(uint32_t a) : Wrapped<uint32_t>(a) {} };
+class QuestId : public Wrapped<uint16_t> { public: constexpr QuestId() : Wrapped<uint16_t>() {} protected: constexpr explicit QuestId(uint16_t a) : Wrapped<uint16_t>(a) {} };
+bool impl_extract(XString str, GmLevel *lvl);
class GmLevel
{
uint32_t bits;
- friend bool extract(XString str, GmLevel *lvl) { return extract(str, &lvl->bits); }
+ friend bool impl_extract(XString str, GmLevel *lvl);
constexpr explicit
GmLevel(uint32_t b) : bits(b) {}
constexpr explicit
diff --git a/src/mmo/ids.py b/src/mmo/ids.py
index 89392b1..a98920f 100644
--- a/src/mmo/ids.py
+++ b/src/mmo/ids.py
@@ -5,6 +5,7 @@ for s in [
'PartyId',
'ItemNameId',
'BlockId',
+ 'QuestId',
]:
class OtherId(object):
__slots__ = ('_value')
diff --git a/src/mmo/login.t.hpp b/src/mmo/login.t.hpp
new file mode 100644
index 0000000..f2c775a
--- /dev/null
+++ b/src/mmo/login.t.hpp
@@ -0,0 +1,44 @@
+#pragma once
+// login.t.hpp - externally useful types from login
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+#include <cstdint>
+
+#include "../generic/enum.hpp"
+
+
+namespace tmwa
+{
+namespace e
+{
+enum class VERSION_2 : uint8_t
+{
+ /// client supports updatehost
+ UPDATEHOST = 0x01,
+ /// send servers in forward order
+ SERVERORDER = 0x02,
+};
+ENUM_BITWISE_OPERATORS(VERSION_2)
+}
+using e::VERSION_2;
+} // namespace tmwa
diff --git a/src/mmo/mmo.cpp b/src/mmo/mmo.cpp
deleted file mode 100644
index aafa431..0000000
--- a/src/mmo/mmo.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "mmo.hpp"
-// mmo.cpp - dummy file to make Make dependencies work
-//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/map/skill.t.hpp b/src/mmo/skill.t.hpp
index d0e3926..21e4059 100644
--- a/src/map/skill.t.hpp
+++ b/src/mmo/skill.t.hpp
@@ -77,6 +77,9 @@ enum class SkillID : uint16_t
// TODO: Remove these!
NEGATIVE = 0xffff,
ZERO = 0x0000,
+ // this is probably the remains of the 'basic' skill,
+ // which has since been partially split into emote, trade, and party,
+ // but the confusion is caused by the fact that it also covered attacks.
ONE = 0x0001,
// Basic skills.
diff --git a/src/mmo/strs.cpp b/src/mmo/strs.cpp
index 71dceec..d780702 100644
--- a/src/mmo/strs.cpp
+++ b/src/mmo/strs.cpp
@@ -18,9 +18,15 @@
// 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 "../io/cxxstdio.hpp"
+
#include "../poison.hpp"
namespace tmwa
{
+VString<49> convert_for_printf(NpcEvent ev)
+{
+ return STRNPRINTF(50, "%s::%s"_fmt, ev.npc, ev.label);
+}
} // namespace tmwa
diff --git a/src/mmo/strs.hpp b/src/mmo/strs.hpp
index fea0c98..6a132c2 100644
--- a/src/mmo/strs.hpp
+++ b/src/mmo/strs.hpp
@@ -123,4 +123,37 @@ CharName stringish<CharName>(VString<23> iv)
{
return CharName(iv);
}
+
+struct MobName : VString<23> {};
+struct NpcName : VString<23> {};
+struct ScriptLabel : VString<23> {};
+struct ItemName : VString<23> {};
+
+// formerly VString<49>, as name::label
+struct NpcEvent
+{
+ NpcName npc;
+ ScriptLabel label;
+
+ explicit operator bool()
+ {
+ return npc || label;
+ }
+ bool operator !()
+ {
+ return !bool(*this);
+ }
+
+ friend bool operator == (const NpcEvent& l, const NpcEvent& r)
+ {
+ return l.npc == r.npc && l.label == r.label;
+ }
+
+ friend bool operator < (const NpcEvent& l, const NpcEvent& r)
+ {
+ return l.npc < r.npc || (l.npc == r.npc && l.label < r.label);
+ }
+
+ friend VString<49> convert_for_printf(NpcEvent ev);
+};
} // namespace tmwa
diff --git a/src/mmo/utils.hpp b/src/mmo/utils.hpp
deleted file mode 100644
index fc3ea74..0000000
--- a/src/mmo/utils.hpp
+++ /dev/null
@@ -1,167 +0,0 @@
-#pragma once
-// utils.hpp - Useful stuff that hasn't been categorized.
-//
-// Copyright © ????-2004 Athena Dev Teams
-// Copyright © 2004-2011 The Mana World Development Team
-// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "fwd.hpp"
-
-#include <cstring>
-#include <ctime>
-
-#include <type_traits>
-
-#include "../ints/little.hpp"
-
-#include "../strings/fwd.hpp"
-#include "../strings/vstring.hpp"
-
-#include "../generic/operators.hpp"
-
-#include "../io/fwd.hpp"
-
-
-namespace tmwa
-{
-template<class T>
-struct is_trivially_copyable
-: std::integral_constant<bool,
- // come back when GCC actually implements the public traits properly
- __has_trivial_copy(T)
- && __has_trivial_assign(T)
- && __has_trivial_destructor(T)>
-{};
-
-bool e_mail_check(XString email);
-int config_switch (ZString str);
-
-template<class T>
-void really_memzero_this(T *v)
-{
- static_assert(is_trivially_copyable<T>::value, "only for mostly-pod types");
- static_assert(std::is_class<T>::value || std::is_union<T>::value, "Only for user-defined structures (for now)");
- memset(v, '\0', sizeof(*v));
-}
-template<class T, size_t n>
-void really_memzero_this(T (&)[n]) = delete;
-
-// Exists in place of time_t, to give it a predictable printf-format.
-// (on x86 and amd64, time_t == long, but not on x32)
-static_assert(sizeof(long long) >= sizeof(time_t), "long long >= time_t");
-struct TimeT : Comparable
-{
- long long value;
-
- // conversion
- TimeT(time_t t=0) : value(t) {}
- TimeT(struct tm t) : value(timegm(&t)) {}
- operator time_t() const { return value; }
- operator struct tm() const { time_t v = value; return *gmtime(&v); }
-
- explicit operator bool() const { return value; }
- bool operator !() const { return !value; }
-
- // prevent surprises
- template<class T>
- TimeT(T) = delete;
- template<class T>
- operator T() const = delete;
-
- static
- TimeT now()
- {
- // poisoned, but this is still in header-land
- return time(nullptr);
- }
-
- bool error() const
- {
- return value == -1;
- }
- bool okay() const
- {
- return !error();
- }
-};
-
-inline
-long long convert_for_printf(TimeT t)
-{
- return t.value;
-}
-
-// 2038 problem
-inline __attribute__((warn_unused_result))
-bool native_to_network(Little32 *net, TimeT nat)
-{
- time_t tmp = nat;
- return native_to_network(net, static_cast<uint32_t>(tmp));
-}
-
-inline __attribute__((warn_unused_result))
-bool network_to_native(TimeT *nat, Little32 net)
-{
- uint32_t tmp;
- bool rv = network_to_native(&tmp, net);
- *nat = static_cast<time_t>(tmp);
- return rv;
-}
-
-inline __attribute__((warn_unused_result))
-bool native_to_network(Little64 *net, TimeT nat)
-{
- time_t tmp = nat;
- return native_to_network(net, static_cast<uint64_t>(tmp));
-}
-
-inline __attribute__((warn_unused_result))
-bool network_to_native(TimeT *nat, Little64 net)
-{
- uint64_t tmp;
- bool rv = network_to_native(&tmp, net);
- *nat = static_cast<time_t>(tmp);
- return rv;
-}
-
-
-struct timestamp_seconds_buffer : VString<19> {};
-struct timestamp_milliseconds_buffer : VString<23> {};
-void stamp_time(timestamp_seconds_buffer&, const TimeT *t=nullptr);
-void stamp_time(timestamp_milliseconds_buffer&);
-
-void log_with_timestamp(io::WriteFile& out, XString line);
-
-// TODO VString?
-#define TIMESTAMP_DUMMY "YYYY-MM-DD HH:MM:SS"
-static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer),
- "timestamp size");
-#define WITH_TIMESTAMP(str) str TIMESTAMP_DUMMY
-// str: prefix: YYYY-MM-DD HH:MM:SS
-// sizeof: 01234567890123456789012345678
-// str + sizeof: ^
-// -1: ^
-// there's probably a better way to do this now
-#define REPLACE_TIMESTAMP(str, t) \
- stamp_time( \
- reinterpret_cast<timestamp_seconds_buffer *>( \
- str + sizeof(str) \
- )[-1], \
- &t \
- )
-} // namespace tmwa
diff --git a/src/mmo/version.cpp b/src/mmo/version.cpp
index 2e337c1..f91b748 100644
--- a/src/mmo/version.cpp
+++ b/src/mmo/version.cpp
@@ -24,7 +24,7 @@
#include "../strings/xstring.hpp"
-#include "extract.hpp"
+#include "../io/extract.hpp"
#include "../poison.hpp"
@@ -66,11 +66,36 @@ Version CURRENT_MAP_SERVER_VERSION =
LString CURRENT_VERSION_STRING = VERSION_STRING;
-bool extract(XString str, Version *vers)
+bool impl_extract(XString str, Version *vers)
{
*vers = {};
- // TODO should I try to extract dev and vend also?
- // It would've been useful during the magic migration.
+ // versions look like:
+ // 1.2.3 (release)
+ // 1.2.3+5 (vendor patches)
+ // 1.2.3-4 (dev patches)
+ // 1.2.3-4+5 (dev patches + vendor patches)
+ XString a, b;
+ if (extract(str, record<'+'>(&a, &b)))
+ {
+ if (!extract(b, &vers->vend))
+ {
+ return false;
+ }
+ str = a;
+ }
+ if (extract(str, record<'-'>(&a, &b)))
+ {
+ if (!extract(b, &vers->devel))
+ {
+ return false;
+ }
+ str = a;
+ }
return extract(str, record<'.'>(&vers->major, &vers->minor, &vers->patch));
}
+
+LString VERSION_INFO_HEADER = "This server code consists of Free Software under GPL3&AGPL3"_s;
+LString VERSION_INFO_COMMIT = "This is commit " VERSION_HASH ", also known as " VERSION_FULL ""_s;
+LString VERSION_INFO_NUMBER = "The version is " VERSION_STRING ""_s;
+LString VERSION_INFO_URL = "For source, see [@@" VENDOR_SOURCE "|" VENDOR_SOURCE "@@]"_s;
} // namespace tmwa
diff --git a/src/mmo/version.hpp b/src/mmo/version.hpp
index 440dce6..a09953f 100644
--- a/src/mmo/version.hpp
+++ b/src/mmo/version.hpp
@@ -24,8 +24,6 @@
#include <cstdint>
-#include "../strings/fwd.hpp"
-
namespace tmwa
{
@@ -89,5 +87,10 @@ extern Version CURRENT_MAP_SERVER_VERSION;
extern LString CURRENT_VERSION_STRING;
-bool extract(XString str, Version *vers);
+bool impl_extract(XString str, Version *vers);
+
+extern LString VERSION_INFO_HEADER;
+extern LString VERSION_INFO_COMMIT;
+extern LString VERSION_INFO_NUMBER;
+extern LString VERSION_INFO_URL;
} // namespace tmwa
diff --git a/src/monitor/main.cpp b/src/monitor/main.cpp
deleted file mode 100644
index ec1139a..0000000
--- a/src/monitor/main.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-// monitor/main.cpp - Old daemon to restart servers when they crashed.
-//
-// Copyright © ???? Bartosz Waszak <waszi@evil.org.pl>
-// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 <sys/wait.h>
-
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <csignal>
-#include <cstdlib>
-
-#include "../strings/mstring.hpp"
-#include "../strings/astring.hpp"
-#include "../strings/zstring.hpp"
-#include "../strings/xstring.hpp"
-#include "../strings/literal.hpp"
-
-#include "../io/cxxstdio.hpp"
-#include "../io/fd.hpp"
-#include "../io/read.hpp"
-
-#include "../mmo/config_parse.hpp"
-#include "../mmo/utils.hpp"
-
-#include "../poison.hpp"
-
-#define LOGIN_SERVER "./login-server"_s
-#define MAP_SERVER "./map-server"_s
-#define CHAR_SERVER "./char-server"_s
-#define CONFIG "conf/eathena-monitor.conf"_s
-
-
-namespace tmwa
-{
-// initialiized to $HOME/tmwserver
-static
-AString workdir;
-//the rest are relative to workdir
-static
-AString login_server = LOGIN_SERVER;
-static
-AString map_server = MAP_SERVER;
-static
-AString char_server = CHAR_SERVER;
-
-static
-pid_t pid_login, pid_map, pid_char;
-
-static
-AString make_path(XString base, XString path)
-{
- MString m;
- m += base;
- m += '/';
- m += path;
- return AString(m);
-}
-
-static
-bool parse_option(XString name, ZString value)
-{
- if (name == "login_server"_s)
- login_server = value;
- else if (name == "map_server"_s)
- map_server = value;
- else if (name == "char_server"_s)
- char_server = value;
- else if (name == "workdir"_s)
- workdir = value;
- else
- {
- FPRINTF(stderr, "WARNING: ingnoring invalid option '%s' : '%s'\n"_fmt,
- AString(name), value);
- return false;
- }
- return true;
-}
-
-static
-bool read_config(ZString filename)
-{
- bool rv = true;
- io::ReadFile in(filename);
- if (!in.is_open())
- {
- FPRINTF(stderr, "Monitor config file not found: %s\n"_fmt, filename);
- exit(1);
- }
-
- AString line;
- while (in.getline(line))
- {
- if (is_comment(line))
- continue;
- XString name;
- ZString value;
- if (!config_split(line, &name, &value))
- {
- PRINTF("Bad line: %s\n"_fmt, line);
- rv = false;
- continue;
- }
-
- if (!parse_option(name, value))
- {
- PRINTF("Bad key/value: %s\n"_fmt, line);
- rv = false;
- continue;
- }
- }
- return rv;
-}
-
-static
-pid_t start_process(ZString exec)
-{
- const char *args[2] = {exec.c_str(), nullptr};
- pid_t pid = fork();
- if (pid == -1)
- {
- FPRINTF(stderr, "Failed to fork"_fmt);
- return 0;
- }
- if (pid == 0)
- {
- DIAG_PUSH();
- DIAG_I(cast_qual);
- execv(exec.c_str(), const_cast<char **>(args));
- DIAG_POP();
- perror("Failed to exec");
- kill(getppid(), SIGABRT);
- exit(1);
- }
- return pid;
-}
-
-// Kill all children with the same signal we got, then ourself.
-static
-void stop_process(int sig)
-{
- if (pid_map)
- kill(pid_map, sig);
- if (pid_login)
- kill(pid_login, sig);
- if (pid_char)
- kill(pid_char, sig);
- DIAG_PUSH();
- DIAG_I(old_style_cast);
- DIAG_I(zero_as_null_pointer_constant);
- signal(sig, SIG_DFL);
- DIAG_POP();
- raise(sig);
-}
-} // namespace tmwa
-
-int main(int argc, char *argv[])
-{
- using namespace tmwa;
- // These are all the signals we are likely to get
- // The shell handles stop/cont
- signal(SIGTERM, stop_process);
- signal(SIGINT, stop_process);
- signal(SIGQUIT, stop_process);
- signal(SIGABRT, stop_process);
-
- workdir = make_path(ZString(strings::really_construct_from_a_pointer, getenv("HOME"), nullptr), "tmwserver"_s);
-
- ZString config = CONFIG;
- if (argc > 1)
- config = ZString(strings::really_construct_from_a_pointer, argv[1], nullptr);
- read_config(config);
-
- if (chdir(workdir.c_str()) < 0)
- {
- perror("Failed to change directory");
- exit(1);
- }
-
- PRINTF("Starting:\n"_fmt);
- PRINTF("* workdir: %s\n"_fmt, workdir);
- PRINTF("* login_server: %s\n"_fmt, login_server);
- PRINTF("* char_server: %s\n"_fmt, char_server);
- PRINTF("* map_server: %s\n"_fmt, map_server);
- {
- //make sure all possible file descriptors are free for use by the servers
- //if there are file descriptors higher than the max open from before the limit dropped, that's not our problem
- io::FD fd = io::FD::sysconf_SC_OPEN_MAX();
- while ((fd = fd.prev()) > io::FD::stderr())
- {
- if (fd.close() == 0)
- FPRINTF(stderr, "close fd %d\n"_fmt, fd.uncast_dammit());
- }
- fd = io::FD::open("/dev/null"_s, O_RDWR);
- if (fd == io::FD())
- {
- perror("open /dev/null");
- exit(1);
- }
- fd.dup2(io::FD::stdin());
- fd.dup2(io::FD::stdout());
- fd.close();
- }
- while (1)
- {
- // write stuff to stderr
- timestamp_seconds_buffer timestamp;
- stamp_time(timestamp);
-
- if (!pid_login)
- {
- pid_login = start_process(login_server);
- FPRINTF(stderr, "[%s] forked login server: %lu\n"_fmt,
- timestamp, static_cast<unsigned long>(pid_login));
- }
- if (!pid_char)
- {
- pid_char = start_process(char_server);
- FPRINTF(stderr, "[%s] forked char server: %lu\n"_fmt,
- timestamp, static_cast<unsigned long>(pid_char));
- }
- if (!pid_map)
- {
- pid_map = start_process(map_server);
- FPRINTF(stderr, "[%s] forked map server: %lu\n"_fmt,
- timestamp, static_cast<unsigned long>(pid_map));
- }
- pid_t dead = wait(nullptr);
- if (dead == -1)
- {
- perror("Failed to wait for child");
- exit(1);
- }
- if (pid_login == dead)
- pid_login = 0;
- if (pid_char == dead)
- pid_char = 0;
- if (pid_map == dead)
- pid_map = 0;
- }
-}
diff --git a/src/net/fwd.hpp b/src/net/fwd.hpp
index 2097772..5de8450 100644
--- a/src/net/fwd.hpp
+++ b/src/net/fwd.hpp
@@ -20,6 +20,13 @@
#include "../sanity.hpp"
+#include "../ints/fwd.hpp" // rank 1
+#include "../strings/fwd.hpp" // rank 1
+#include "../compat/fwd.hpp" // rank 2
+#include "../generic/fwd.hpp" // rank 3
+#include "../io/fwd.hpp" // rank 4
+// net/fwd.hpp is rank 5
+
namespace tmwa
{
@@ -28,6 +35,4 @@ class Session;
class IP4Address;
class TimerData;
-
-enum class RecvResult;
} // namespace tmwa
diff --git a/src/net/ip.cpp b/src/net/ip.cpp
index bfc2028..bedbca8 100644
--- a/src/net/ip.cpp
+++ b/src/net/ip.cpp
@@ -22,15 +22,14 @@
#include "../strings/vstring.hpp"
#include "../io/cxxstdio.hpp"
-
-#include "../mmo/extract.hpp"
+#include "../io/extract.hpp"
#include "../poison.hpp"
namespace tmwa
{
-bool extract(XString str, IP4Address *rv)
+bool impl_extract(XString str, IP4Address *rv)
{
if (str.endswith('.'))
return false;
@@ -43,7 +42,7 @@ bool extract(XString str, IP4Address *rv)
return false;
}
-bool extract(XString str, IP4Mask *rv)
+bool impl_extract(XString str, IP4Mask *rv)
{
IP4Address a, m;
unsigned b;
@@ -106,6 +105,33 @@ bool extract(XString str, IP4Mask *rv)
return true;
}
+bool impl_extract(XString str, std::vector<IP4Mask> *iv)
+{
+ if (str == "all"_s)
+ {
+ iv->clear();
+ iv->push_back(IP4Mask());
+ return true;
+ }
+ if (str == "clear"_s)
+ {
+ iv->clear();
+ return true;
+ }
+ // don't add if already 'all'
+ if (iv->size() == 1 && iv->front().mask() == IP4Address())
+ {
+ return true;
+ }
+ IP4Mask mask;
+ if (extract(str, &mask))
+ {
+ iv->push_back(mask);
+ return true;
+ }
+ return false;
+}
+
VString<15> convert_for_printf(IP4Address a_)
{
const uint8_t *a = a_.bytes();
diff --git a/src/net/ip.hpp b/src/net/ip.hpp
index e9e71f4..7508c08 100644
--- a/src/net/ip.hpp
+++ b/src/net/ip.hpp
@@ -25,7 +25,7 @@
#include <cstddef>
#include <cstdint>
-#include "../strings/fwd.hpp"
+#include <vector>
namespace tmwa
@@ -160,7 +160,7 @@ IP4Address IP4_BROADCAST({255, 255, 255, 255});
VString<15> convert_for_printf(IP4Address a);
VString<31> convert_for_printf(IP4Mask m);
-bool extract(XString str, IP4Address *iv);
-
-bool extract(XString str, IP4Mask *iv);
+bool impl_extract(XString str, IP4Address *iv);
+bool impl_extract(XString str, IP4Mask *iv);
+bool impl_extract(XString str, std::vector<IP4Mask> *iv);
} // namespace tmwa
diff --git a/src/net/ip_test.cpp b/src/net/ip_test.cpp
index 419dc03..2b9bcad 100644
--- a/src/net/ip_test.cpp
+++ b/src/net/ip_test.cpp
@@ -24,6 +24,7 @@
#include "../strings/literal.hpp"
#include "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
#include "../poison.hpp"
diff --git a/src/net/socket.cpp b/src/net/socket.cpp
index a01cd81..fce45fb 100644
--- a/src/net/socket.cpp
+++ b/src/net/socket.cpp
@@ -34,10 +34,6 @@
#include "../io/cxxstdio.hpp"
-// TODO get rid of ordering violations
-#include "../mmo/utils.hpp"
-#include "../mmo/core.hpp"
-
#include "timer.hpp"
#include "../poison.hpp"
@@ -399,7 +395,7 @@ void realloc_fifo(Session *s, size_t rfifo_size, size_t wfifo_size)
}
}
-void do_sendrecv(interval_t next_ms)
+bool do_sendrecv(interval_t next_ms)
{
bool any = false;
io::FD_Set rfd = readfds, wfd;
@@ -419,9 +415,9 @@ void do_sendrecv(interval_t next_ms)
{
PRINTF("Shutting down - nothing to do\n"_fmt);
// TODO hoist this
- runflag = false;
+ return false;
}
- return;
+ return true;
}
struct timeval timeout;
{
@@ -431,7 +427,7 @@ void do_sendrecv(interval_t next_ms)
timeout.tv_usec = next_us.count();
}
if (io::FD_Set::select(fd_max, &rfd, &wfd, nullptr, &timeout) <= 0)
- return;
+ return true;
for (io::FD i : iter_fds())
{
Session *s = get_session(i);
@@ -451,9 +447,10 @@ void do_sendrecv(interval_t next_ms)
s->func_recv(s);
}
}
+ return true;
}
-void do_parsepacket(void)
+bool do_parsepacket(void)
{
for (io::FD i : iter_fds())
{
@@ -483,5 +480,6 @@ void do_parsepacket(void)
/// Reclaim buffer space for what was read
RFIFOFLUSH(s);
}
+ return true;
}
} // namespace tmwa
diff --git a/src/net/socket.hpp b/src/net/socket.hpp
index 576ef85..d6caefd 100644
--- a/src/net/socket.hpp
+++ b/src/net/socket.hpp
@@ -22,20 +22,19 @@
#include "fwd.hpp"
-#include <algorithm>
-
#include <sys/select.h>
+#include <algorithm>
#include <memory>
-#include "../compat/iter.hpp"
-#include "../compat/rawmem.hpp"
-#include "../compat/time_t.hpp"
-
#include "../strings/astring.hpp"
#include "../strings/vstring.hpp"
#include "../strings/xstring.hpp"
+#include "../compat/iter.hpp"
+#include "../compat/rawmem.hpp"
+#include "../compat/time_t.hpp"
+
#include "../generic/dumb_ptr.hpp"
#include "../io/fd.hpp"
@@ -125,8 +124,8 @@ public:
io::FD fd;
- friend void do_sendrecv(interval_t next);
- friend void do_parsepacket(void);
+ friend bool do_sendrecv(interval_t next);
+ friend bool do_parsepacket(void);
friend void delete_session(Session *);
};
@@ -171,7 +170,7 @@ void delete_session(Session *);
/// Make a the internal queues bigger
void realloc_fifo(Session *s, size_t rfifo_size, size_t wfifo_size);
/// Update all sockets that can be read/written from the queues
-void do_sendrecv(interval_t next);
+bool do_sendrecv(interval_t next);
/// Call the parser function for every socket that has read data
-void do_parsepacket(void);
+bool do_parsepacket(void);
} // namespace tmwa
diff --git a/src/net/timer.hpp b/src/net/timer.hpp
index 338e339..5e7cc90 100644
--- a/src/net/timer.hpp
+++ b/src/net/timer.hpp
@@ -21,11 +21,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "timer.t.hpp"
-
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
namespace tmwa
{
diff --git a/src/net/timer.py b/src/net/timer.py
new file mode 100644
index 0000000..2ccb3bb
--- /dev/null
+++ b/src/net/timer.py
@@ -0,0 +1,117 @@
+class duration(object):
+ __slots__ = ('_whole', '_frac', '_units')
+ name = 'std::chrono::duration'
+ enabled = True
+
+ def __init__(self, value):
+ from decimal import Decimal
+
+ rep = int(value['__r'])
+ ratio = value.type.template_argument(1)
+ num = int(ratio.template_argument(0))
+ den = int(ratio.template_argument(1))
+ # this will fail on duration<float>
+ value = Decimal(rep) * num / den
+ whole = int(value)
+ self._whole = whole
+ self._frac = value - whole
+ units = {
+ (1, 1000*1000*1000): ('nanoseconds', '_ns'),
+ (1, 1000*1000): ('microseconds', '_us'),
+ (1, 1000): ('milliseconds', '_ms'),
+ (1, 1): ('seconds', '_s'),
+ (60, 1): ('minutes', '_min'),
+ (60*60, 1): ('hours', '_h'),
+ (24*60*60, 1): ('duration<int, ratio<%d, %d>>', '_d'),
+ # days don't exist (probably because of leap seconds)
+ }
+ self._units = units.get((num, den)) or ('duration<???, ratio<%d, %d>>' % (num, den), '_?')
+
+ def to_string(self):
+ whole = self._whole
+ frac = self._frac
+ cu, su = self._units
+ if not whole and not frac:
+ return '0%s' % su
+ s = whole
+ min = s // 60
+ s %= 60
+ h = min // 60
+ min %= 60
+ d = h // 24
+ h %= 24
+ msx = frac * 1000
+ ms = int(msx)
+ usx = (msx - ms) * 1000
+ us = int(usx)
+ nsx = (usx - us) * 1000
+ ns = int(nsx)
+ bits = [
+ '%d_d' % d if d else None,
+ '%d_h' % h if h else None,
+ '%d_min' % min if min else None,
+ '%d_s' % s if s else None,
+ '%d_ms' % ms if ms else None,
+ '%d_us' % us if us else None,
+ '%d_ns' % ns if ns else None,
+ ]
+ body = ' + '.join(b for b in bits if b is not None)
+ if not body.endswith(su):
+ body = '%s(%s)' % (cu, body)
+ elif '+' in body:
+ body = '(%s)' % body
+ return body
+
+ tests = [
+ ('std::chrono::nanoseconds(0)', '0_ns'),
+ ('std::chrono::microseconds(0)', '0_us'),
+ ('std::chrono::milliseconds(0)', '0_ms'),
+ ('std::chrono::seconds(0)', '0_s'),
+ ('std::chrono::minutes(0)', '0_min'),
+ ('std::chrono::hours(0)', '0_h'),
+ ('std::chrono::duration<int, std::ratio<60*60*24>>(0)', '0_d'),
+
+ ('std::chrono::nanoseconds(1)', '1_ns'),
+ ('std::chrono::microseconds(1)', '1_us'),
+ ('std::chrono::milliseconds(1)', '1_ms'),
+ ('std::chrono::seconds(1)', '1_s'),
+ ('std::chrono::minutes(1)', '1_min'),
+ ('std::chrono::hours(1)', '1_h'),
+ ('std::chrono::duration<int, std::ratio<60*60*24>>(1)', '1_d'),
+
+ ('std::chrono::nanoseconds(1)', '1_ns'),
+ ('std::chrono::microseconds(1)', '1_us'),
+ ('std::chrono::milliseconds(1)', '1_ms'),
+ ('std::chrono::seconds(1)', '1_s'),
+ ('std::chrono::minutes(1)', '1_min'),
+ ('std::chrono::hours(1)', '1_h'),
+ ('std::chrono::duration<int, std::ratio<60*60*24>>(1)', '1_d'),
+
+ ('std::chrono::nanoseconds(3)', '3_ns'),
+ ('std::chrono::microseconds(3)', '3_us'),
+ ('std::chrono::milliseconds(3)', '3_ms'),
+ ('std::chrono::seconds(3)', '3_s'),
+ ('std::chrono::minutes(3)', '3_min'),
+ ('std::chrono::hours(3)', '3_h'),
+ ('std::chrono::duration<int, std::ratio<60*60*24>>(3)', '3_d'),
+
+ ('std::chrono::nanoseconds(1000)', 'nanoseconds(1_us)'),
+ ('std::chrono::microseconds(1000)', 'microseconds(1_ms)'),
+ ('std::chrono::milliseconds(1000)', 'milliseconds(1_s)'),
+ ('std::chrono::seconds(60)', 'seconds(1_min)'),
+ ('std::chrono::minutes(60)', 'minutes(1_h)'),
+ ('std::chrono::hours(24)', 'hours(1_d)'),
+
+ ('std::chrono::nanoseconds(1001)', '(1_us + 1_ns)'),
+ ('std::chrono::microseconds(1001)', '(1_ms + 1_us)'),
+ ('std::chrono::milliseconds(1001)', '(1_s + 1_ms)'),
+ ('std::chrono::seconds(61)', '(1_min + 1_s)'),
+ ('std::chrono::minutes(61)', '(1_h + 1_min)'),
+ ('std::chrono::hours(25)', '(1_d + 1_h)'),
+
+ ('std::chrono::nanoseconds(1001*1000)', 'nanoseconds(1_ms + 1_us)'),
+ ('std::chrono::microseconds(1001*1000)', 'microseconds(1_s + 1_ms)'),
+ ('std::chrono::milliseconds(61*1000)', 'milliseconds(1_min + 1_s)'),
+ ('std::chrono::seconds(61*60)', 'seconds(1_h + 1_min)'),
+ ('std::chrono::minutes(25*60)', 'minutes(1_d + 1_h)'),
+ ]
diff --git a/src/net/timestamp-utils.cpp b/src/net/timestamp-utils.cpp
new file mode 100644
index 0000000..b5873ca
--- /dev/null
+++ b/src/net/timestamp-utils.cpp
@@ -0,0 +1,72 @@
+#include "timestamp-utils.hpp"
+// timestamp-utils.cpp - Useful stuff that hasn't been categorized.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 <sys/time.h>
+
+#include <algorithm>
+
+#include "../strings/xstring.hpp"
+
+#include "../compat/time_t.hpp"
+
+#include "../io/write.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+static_assert(sizeof(timestamp_seconds_buffer) == 20, "seconds buffer");
+static_assert(sizeof(timestamp_milliseconds_buffer) == 24, "millis buffer");
+
+void stamp_time(timestamp_seconds_buffer& out, const TimeT *t)
+{
+ struct tm when = t ? *t : TimeT::now();
+ char buf[20];
+ strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &when);
+ out = stringish<timestamp_seconds_buffer>(VString<19>(strings::really_construct_from_a_pointer, buf));
+}
+void stamp_time(timestamp_milliseconds_buffer& out)
+{
+ struct timeval tv;
+ gettimeofday(&tv, nullptr);
+ struct tm when = TimeT(tv.tv_sec);
+ char buf[24];
+ strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &when);
+ sprintf(buf + 19, ".%03d", static_cast<int>(tv.tv_usec / 1000));
+ out = stringish<timestamp_milliseconds_buffer>(VString<23>(strings::really_construct_from_a_pointer, buf));
+}
+
+void log_with_timestamp(io::WriteFile& out, XString line)
+{
+ if (!line)
+ {
+ out.put('\n');
+ return;
+ }
+ timestamp_milliseconds_buffer tmpstr;
+ stamp_time(tmpstr);
+ out.really_put(tmpstr.data(), tmpstr.size());
+ out.really_put(": ", 2);
+ out.put_line(line);
+}
+} // namespace tmwa
diff --git a/src/net/timestamp-utils.hpp b/src/net/timestamp-utils.hpp
new file mode 100644
index 0000000..f5b5dce
--- /dev/null
+++ b/src/net/timestamp-utils.hpp
@@ -0,0 +1,54 @@
+#pragma once
+// timestamp-utils.hpp - Useful stuff that hasn't been categorized.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// 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 "fwd.hpp"
+
+#include "../strings/vstring.hpp"
+
+
+namespace tmwa
+{
+struct timestamp_seconds_buffer : VString<19> {};
+struct timestamp_milliseconds_buffer : VString<23> {};
+void stamp_time(timestamp_seconds_buffer&, const TimeT *t=nullptr);
+void stamp_time(timestamp_milliseconds_buffer&);
+
+void log_with_timestamp(io::WriteFile& out, XString line);
+
+// TODO VString?
+#define TIMESTAMP_DUMMY "YYYY-MM-DD HH:MM:SS"
+static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer),
+ "timestamp size");
+#define WITH_TIMESTAMP(str) str TIMESTAMP_DUMMY
+// str: prefix: YYYY-MM-DD HH:MM:SS
+// sizeof: 01234567890123456789012345678
+// str + sizeof: ^
+// -1: ^
+// there's probably a better way to do this now
+#define REPLACE_TIMESTAMP(str, t) \
+ stamp_time( \
+ reinterpret_cast<timestamp_seconds_buffer *>( \
+ str + sizeof(str) \
+ )[-1], \
+ &t \
+ )
+} // namespace tmwa
diff --git a/src/generic/array.cpp b/src/proto-base/fwd.hpp
index 3063569..1790717 100644
--- a/src/generic/array.cpp
+++ b/src/proto-base/fwd.hpp
@@ -1,5 +1,5 @@
-#include "array.hpp"
-// array.cpp - A simple bounds-checked array.
+#pragma once
+// proto-base/fwd.hpp - list of type names for network protocol base
//
// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
//
@@ -18,9 +18,15 @@
// 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 "../poison.hpp"
+#include "../sanity.hpp"
+
+#include "../strings/fwd.hpp" // rank 1
+#include "../generic/fwd.hpp" // rank 3
+#include "../mmo/fwd.hpp" // rank 6
+// proto-base/fwd.hpp is rank 7
namespace tmwa
{
+// meh, add more when I feel like it
} // namespace tmwa
diff --git a/src/proto-base/net-array.hpp b/src/proto-base/net-array.hpp
new file mode 100644
index 0000000..814d257
--- /dev/null
+++ b/src/proto-base/net-array.hpp
@@ -0,0 +1,53 @@
+#pragma once
+// proto2/net-array.hpp - Special logic for fixed-size arrays in packets.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#include "../generic/array.hpp"
+
+
+namespace tmwa
+{
+template<class T, size_t N>
+struct NetArray
+{
+ T data[N];
+};
+template<class T, class U, class I>
+bool native_to_network(NetArray<T, I::alloc_size> *network, GenericArray<U, I> native)
+{
+ for (size_t i = 0; i < I::alloc_size; ++i)
+ {
+ if (!native_to_network(&(*network).data[i], native[I::offset_to_index(i)]))
+ return false;
+ }
+ return true;
+}
+template<class T, class U, class I>
+bool network_to_native(GenericArray<U, I> *native, NetArray<T, I::alloc_size> network)
+{
+ for (size_t i = 0; i < I::alloc_size; ++i)
+ {
+ if (!network_to_native(&(*native)[I::offset_to_index(i)], network.data[i]))
+ return false;
+ }
+ return true;
+}
+} // namespace tmwa
diff --git a/src/proto-base/net-neutral.hpp b/src/proto-base/net-neutral.hpp
new file mode 100644
index 0000000..523748c
--- /dev/null
+++ b/src/proto-base/net-neutral.hpp
@@ -0,0 +1,38 @@
+#pragma once
+// proto2/net-neutral.hpp - Convert nothing across the network
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+
+namespace tmwa
+{
+template<class T>
+bool native_to_network(T *network, T native)
+{
+ *network = native;
+ return true;
+}
+template<class T>
+bool network_to_native(T *native, T network)
+{
+ *native = network;
+ return true;
+}
+} // namespace tmwa
diff --git a/src/proto-base/net-skewed-length.hpp b/src/proto-base/net-skewed-length.hpp
new file mode 100644
index 0000000..84af508
--- /dev/null
+++ b/src/proto-base/net-skewed-length.hpp
@@ -0,0 +1,46 @@
+#pragma once
+// proto2/net-skewed-length.hpp - Deprecated logic for skewed-size packets.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#include <cstddef>
+
+
+namespace tmwa
+{
+template<class T, size_t N>
+struct SkewedLength
+{
+ T data;
+};
+template<class T, size_t N, class U>
+bool native_to_network(SkewedLength<T, N> *network, U native)
+{
+ native -= N;
+ return native_to_network(&network->data, native);
+}
+template<class T, size_t N, class U>
+bool network_to_native(U *native, SkewedLength<T, N> network)
+{
+ bool rv = network_to_native(native, network.data);
+ *native += N;
+ return rv;
+}
+} // namespace tmwa
diff --git a/src/proto-base/net-string.hpp b/src/proto-base/net-string.hpp
new file mode 100644
index 0000000..a9a120d
--- /dev/null
+++ b/src/proto-base/net-string.hpp
@@ -0,0 +1,87 @@
+#pragma once
+// proto2/net-string.hpp - Special logic for fixed-size strings in packets.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#include "../strings/vstring.hpp"
+
+#include "../mmo/strs.hpp"
+
+
+namespace tmwa
+{
+template<size_t N>
+struct NetString
+{
+ char data[N];
+};
+template<size_t N>
+bool native_to_network(NetString<N> *network, VString<N-1> native)
+{
+ // basically WBUF_STRING
+ char *const begin = network->data;
+ char *const end = begin + N;
+ char *const mid = std::copy(native.begin(), native.end(), begin);
+ std::fill(mid, end, '\0');
+ return true;
+}
+template<size_t N>
+bool network_to_native(VString<N-1> *native, NetString<N> network)
+{
+ // basically RBUF_STRING
+ const char *const begin = network.data;
+ const char *const end = begin + N;
+ const char *const mid = std::find(begin, end, '\0');
+ *native = XString(begin, mid, nullptr);
+ return true;
+}
+
+inline
+bool native_to_network(NetString<24> *network, CharName native)
+{
+ VString<23> tmp = native.to__actual();
+ bool rv = native_to_network(network, tmp);
+ return rv;
+}
+inline
+bool network_to_native(CharName *native, NetString<24> network)
+{
+ VString<23> tmp;
+ bool rv = network_to_native(&tmp, network);
+ *native = stringish<CharName>(tmp);
+ return rv;
+}
+
+inline
+bool native_to_network(NetString<16> *network, MapName native)
+{
+ XString tmp = native;
+ bool rv = native_to_network(network, VString<15>(tmp));
+ return rv;
+}
+inline
+bool network_to_native(MapName *native, NetString<16> network)
+{
+ VString<15> tmp;
+ bool rv = network_to_native(&tmp, network);
+ *native = stringish<MapName>(tmp);
+ return rv;
+}
+} // namespace tmwa
diff --git a/src/range/fwd.hpp b/src/range/fwd.hpp
index 646eadb..4bad327 100644
--- a/src/range/fwd.hpp
+++ b/src/range/fwd.hpp
@@ -20,6 +20,8 @@
#include "../sanity.hpp"
+// range/fwd.hpp is rank 1
+
namespace tmwa
{
diff --git a/src/range/slice.cpp b/src/range/slice.cpp
deleted file mode 100644
index f93c19f..0000000
--- a/src/range/slice.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "slice.hpp"
-// slice.cpp - dummy file to make Make dependencies work
-//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/sanity.hpp b/src/sanity.hpp
index c00d9b2..455990a 100644
--- a/src/sanity.hpp
+++ b/src/sanity.hpp
@@ -41,10 +41,10 @@ namespace tmwa
#endif // __GNUC__ == 4
#if not defined(__i386__) and not defined(__x86_64__)
-// Known platform dependencies:
-// endianness for the [RW]FIFO.* macros
-// possibly, some signal-handling
-// some integer sizes (partially fixed for the x32 ABI)
-# error "Unsupported platform, we use x86 / amd64 only"
+// There are no longer any *known* platform-independent bits,
+// but nothing has been tested yet. It compiles, though, so ship it!
+# ifndef QUIET
+# warning "Unsupported platform, upstream uses x86 / amd64 only"
+# endif
#endif // not __i386__
} // namespace tmwa
diff --git a/src/sexpr/fwd.hpp b/src/sexpr/fwd.hpp
index 580b322..b86d9fb 100644
--- a/src/sexpr/fwd.hpp
+++ b/src/sexpr/fwd.hpp
@@ -20,6 +20,10 @@
#include "../sanity.hpp"
+#include "../strings/fwd.hpp" // rank 1
+#include "../io/fwd.hpp" // rank 4
+// sexpr/fwd.hpp is rank 5
+
namespace tmwa
{
diff --git a/src/sexpr/lexer.hpp b/src/sexpr/lexer.hpp
index 63be72d..9b198a0 100644
--- a/src/sexpr/lexer.hpp
+++ b/src/sexpr/lexer.hpp
@@ -22,7 +22,6 @@
#include <vector>
-#include "../strings/fwd.hpp"
#include "../strings/astring.hpp"
#include "../strings/zstring.hpp"
@@ -59,10 +58,13 @@ namespace sexpr
Lexer(ZString filename)
: _in(filename), _current(TOK_EOF), _span(), _depth()
{ adv(); }
- // for unit tests
- Lexer(ZString fake, io::FD fd)
- : _in(fake, fd), _current(TOK_EOF), _span(), _depth()
+ Lexer(io::read_file_from_string, ZString name, XString str)
+ : _in(io::from_string, name, str), _current(TOK_EOF), _span(), _depth()
{ adv(); }
+ Lexer(io::read_file_from_string, ZString name, LString str)
+ : _in(io::from_string, name, str), _current(TOK_EOF), _span(), _depth()
+ { adv(); }
+
Lexeme peek() { return _current; }
void adv() { _current = _adv(); }
ZString val_string() { return _string; }
diff --git a/src/sexpr/lexer_test.cpp b/src/sexpr/lexer_test.cpp
index fdb47f2..d84312e 100644
--- a/src/sexpr/lexer_test.cpp
+++ b/src/sexpr/lexer_test.cpp
@@ -29,22 +29,6 @@
namespace tmwa
{
-static
-io::FD string_pipe(ZString sz)
-{
- io::FD rfd, wfd;
- if (-1 == io::FD::pipe(rfd, wfd))
- return io::FD();
- if (sz.size() != wfd.write(sz.c_str(), sz.size()))
- {
- rfd.close();
- wfd.close();
- return io::FD();
- }
- wfd.close();
- return rfd;
-}
-
TEST(sexpr, escape)
{
EXPECT_EQ(sexpr::escape('\0'), "\\x00"_s);
@@ -73,24 +57,24 @@ TEST(sexpr, escape)
TEST(sexpr, lexer)
{
io::LineSpan span;
- sexpr::Lexer lexer("<lexer-test1>"_s, string_pipe(" foo( ) 123\"\" \n"_s));
+ sexpr::Lexer lexer(io::from_string, "<lexer-test1>"_s, " foo( ) 123\"\" \n"_s);
EXPECT_EQ(lexer.peek(), sexpr::TOK_TOKEN);
EXPECT_EQ(lexer.val_string(), "foo"_s);
- EXPECT_EQ(lexer.span().message_str("error"_s, "test"_s),
+ EXPECT_EQ(lexer.span().error_str("test"_s),
"<lexer-test1>:1:2: error: test\n"
" foo( ) 123\"\" \n"
" ^~~\n"_s
);
lexer.adv();
EXPECT_EQ(lexer.peek(), sexpr::TOK_OPEN);
- EXPECT_EQ(lexer.span().message_str("error"_s, "test"_s),
+ EXPECT_EQ(lexer.span().error_str("test"_s),
"<lexer-test1>:1:5: error: test\n"
" foo( ) 123\"\" \n"
" ^\n"_s
);
lexer.adv();
EXPECT_EQ(lexer.peek(), sexpr::TOK_CLOSE);
- EXPECT_EQ(lexer.span().message_str("error"_s, "test"_s),
+ EXPECT_EQ(lexer.span().error_str("test"_s),
"<lexer-test1>:1:7: error: test\n"
" foo( ) 123\"\" \n"
" ^\n"_s
@@ -98,7 +82,7 @@ TEST(sexpr, lexer)
lexer.adv();
EXPECT_EQ(lexer.peek(), sexpr::TOK_TOKEN);
EXPECT_EQ(lexer.val_string(), "123"_s);
- EXPECT_EQ(lexer.span().message_str("error"_s, "test"_s),
+ EXPECT_EQ(lexer.span().error_str("test"_s),
"<lexer-test1>:1:9: error: test\n"
" foo( ) 123\"\" \n"
" ^~~\n"_s
@@ -106,7 +90,7 @@ TEST(sexpr, lexer)
lexer.adv();
EXPECT_EQ(lexer.peek(), sexpr::TOK_STRING);
EXPECT_EQ(lexer.val_string(), ""_s);
- EXPECT_EQ(lexer.span().message_str("error"_s, "test"_s),
+ EXPECT_EQ(lexer.span().error_str("test"_s),
"<lexer-test1>:1:12: error: test\n"
" foo( ) 123\"\" \n"
" ^~\n"_s
@@ -120,7 +104,7 @@ TEST(sexpr, lexbad)
QuietFd q;
{
io::LineSpan span;
- sexpr::Lexer lexer("<lexer-bad>"_s, string_pipe("(\n"_s));
+ sexpr::Lexer lexer(io::from_string, "<lexer-bad>"_s, "(\n"_s);
EXPECT_EQ(lexer.peek(), sexpr::TOK_OPEN);
lexer.adv();
EXPECT_EQ(lexer.peek(), sexpr::TOK_ERROR);
@@ -135,7 +119,7 @@ TEST(sexpr, lexbad)
})
{
io::LineSpan span;
- sexpr::Lexer lexer("<lexer-bad>"_s, string_pipe(bad));
+ sexpr::Lexer lexer(io::from_string, "<lexer-bad>"_s, bad);
EXPECT_EQ(lexer.peek(), sexpr::TOK_ERROR);
}
}
diff --git a/src/sexpr/parser.hpp b/src/sexpr/parser.hpp
index feed636..cfe8212 100644
--- a/src/sexpr/parser.hpp
+++ b/src/sexpr/parser.hpp
@@ -22,8 +22,6 @@
#include <cstdlib>
-#include "../strings/fwd.hpp"
-
#include "../io/line.hpp"
#include "lexer.hpp"
diff --git a/src/sexpr/parser_test.cpp b/src/sexpr/parser_test.cpp
index 846d425..bbaf5eb 100644
--- a/src/sexpr/parser_test.cpp
+++ b/src/sexpr/parser_test.cpp
@@ -20,32 +20,18 @@
#include <gtest/gtest.h>
+#include "../tests/fdhack.hpp"
+
#include "../poison.hpp"
namespace tmwa
{
-static
-io::FD string_pipe(ZString sz)
-{
- io::FD rfd, wfd;
- if (-1 == io::FD::pipe(rfd, wfd))
- return io::FD();
- if (sz.size() != wfd.write(sz.c_str(), sz.size()))
- {
- rfd.close();
- wfd.close();
- return io::FD();
- }
- wfd.close();
- return rfd;
-}
-
TEST(sexpr, parser)
{
sexpr::SExpr s;
io::LineSpan span;
- sexpr::Lexer lexer("<parser-test1>"_s, string_pipe(" foo( ) 123\"\" \n"_s));
+ sexpr::Lexer lexer(io::from_string, "<parser-test1>"_s, " foo( ) 123\"\" \n"_s);
EXPECT_TRUE(sexpr::parse(lexer, s));
EXPECT_EQ(s._type, sexpr::TOKEN);
@@ -70,7 +56,7 @@ TEST(sexpr, parser)
TEST(sexpr, parselist)
{
sexpr::SExpr s;
- sexpr::Lexer lexer("<parser-test1>"_s, string_pipe("(foo)(bar)\n"_s));
+ sexpr::Lexer lexer(io::from_string, "<parser-test1>"_s, "(foo)(bar)\n"_s);
EXPECT_TRUE(sexpr::parse(lexer, s));
EXPECT_EQ(s._type, sexpr::LIST);
@@ -90,6 +76,7 @@ TEST(sexpr, parselist)
TEST(sexpr, parsebad)
{
+ QuietFd q;
for (LString bad : {
"(\n"_s,
")\n"_s,
@@ -105,7 +92,7 @@ TEST(sexpr, parsebad)
{
sexpr::SExpr s;
io::LineSpan span;
- sexpr::Lexer lexer("<parse-bad>"_s, string_pipe(bad));
+ sexpr::Lexer lexer(io::from_string, "<parse-bad>"_s, bad);
EXPECT_FALSE(sexpr::parse(lexer, s));
EXPECT_EQ(lexer.peek(), sexpr::TOK_ERROR);
}
diff --git a/src/sexpr/union.cpp b/src/sexpr/union_test.cpp
index 6f65012..ca60b49 100644
--- a/src/sexpr/union.cpp
+++ b/src/sexpr/union_test.cpp
@@ -1,5 +1,5 @@
#include "union.hpp"
-// union.cpp - Just include the header file and try to instantiate.
+// union_test.cpp - Just include the header file and try to instantiate.
//
// Copyright © 2012 Ben Longbons <b.r.longbons@gmail.com>
//
diff --git a/src/sexpr/variant.hpp b/src/sexpr/variant.hpp
index fbf9345..0eccc5a 100644
--- a/src/sexpr/variant.hpp
+++ b/src/sexpr/variant.hpp
@@ -33,19 +33,42 @@ namespace tmwa
{
namespace sexpr
{
-#define JOIN(a, b) a##b
-
-#define WITH_VAR(ty, var, expr) \
- for (bool JOIN(var, _guard) = true; JOIN(var, _guard); ) \
- for (ty var = expr; JOIN(var, _guard); JOIN(var, _guard) = false)
-#define MATCH(expr) \
- WITH_VAR(auto&&, _match_var, expr) \
- switch (tmwa::sexpr::VariantFriend::get_state(_match_var))
-#define TYPED_CASE(ty, var, look) \
- break; \
- case tmwa::sexpr::VariantFriend::get_state_for<look, decltype(_match_var)>(): \
- WITH_VAR(ty, var, tmwa::sexpr::VariantFriend::unchecked_get<look>(_match_var))
-#define CASE(ty, var) TYPED_CASE(ty, var, std::remove_const<std::remove_reference<ty>::type>::type)
+#define MATCH_BEGIN(expr) \
+ { \
+ auto&& _match_var = (expr); \
+ switch (tmwa::sexpr::VariantFriend::get_state(_match_var)) \
+ { \
+ { \
+ { \
+ /* }}}} */
+#define MATCH_END() \
+ /* {{{{ */ \
+ } \
+ } \
+ } \
+ (void) _match_var; \
+ }
+
+#define MATCH_CASE(ty, v) \
+ /* {{{{ */ \
+ } \
+ break; \
+ } \
+ { \
+ using _match_case_type = std::remove_const<std::remove_reference<ty>::type>::type; \
+ case tmwa::sexpr::VariantFriend::get_state_for<_match_case_type, decltype(_match_var)>(): \
+ { \
+ ty v = tmwa::sexpr::VariantFriend::unchecked_get<_match_case_type>(_match_var);
+ /* }}}} */
+#define MATCH_DEFAULT() \
+ /* {{{{ */ \
+ } \
+ break; \
+ } \
+ { \
+ default: \
+ { \
+ /* }}}} */
template<class... T>
class Variant
diff --git a/src/sexpr/variant.tcc b/src/sexpr/variant.tcc
index 1f7df03..c370fa4 100644
--- a/src/sexpr/variant.tcc
+++ b/src/sexpr/variant.tcc
@@ -120,6 +120,9 @@ namespace sexpr
}
catch (...)
{
+ // TODO switch from requiring nothrow default construct, to
+ // instead require nothrow moves, and offer the strong exception
+ // guarantee (which is actually easier that the basic one)
#if GCC != 407 // apparent compiler bug, not reduced
// 4.7.2 from wheezy is bad
// 4.7.3 from jessie is good
diff --git a/src/sexpr/variant_test.cpp b/src/sexpr/variant_test.cpp
index bc378aa..c671264 100644
--- a/src/sexpr/variant_test.cpp
+++ b/src/sexpr/variant_test.cpp
@@ -77,42 +77,46 @@ TEST(variant, match)
: sexpr::Variant<Foo, Bar>(Foo(1))
{}
};
+
Sub v1;
- MATCH (v1)
+ MATCH_BEGIN (v1)
{
- // This is not a public API, it's just for testing.
- default:
- FAIL();
-
- CASE(Foo, f)
+ MATCH_DEFAULT ()
+ {
+ FAIL();
+ }
+ MATCH_CASE (Foo, f)
{
(void)f;
SUCCEED();
}
- CASE(Bar, b)
+ MATCH_CASE (Bar, b)
{
(void)b;
FAIL();
}
}
+ MATCH_END ();
+
v1.emplace<Bar>(2);
- MATCH (v1)
+ MATCH_BEGIN (v1)
{
- // This is not a public API, it's just for testing.
- default:
- FAIL();
-
- CASE(Foo, f)
+ MATCH_DEFAULT ()
+ {
+ FAIL();
+ }
+ MATCH_CASE (Foo, f)
{
(void)f;
FAIL();
}
- CASE(Bar, b)
+ MATCH_CASE (Bar, b)
{
(void)b;
SUCCEED();
}
}
+ MATCH_END ();
}
TEST(variant, copymove1)
diff --git a/src/sexpr/void.cpp b/src/sexpr/void.cpp
deleted file mode 100644
index 9f0eeb5..0000000
--- a/src/sexpr/void.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "void.hpp"
-// void.cpp - Just include the header file.
-//
-// Copyright © 2012 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-namespace sexpr
-{
-} // namespace sexpr
-} // namespace tmwa
diff --git a/src/strings/astring.py b/src/strings/astring.py
index f4cbf66..a3306d9 100644
--- a/src/strings/astring.py
+++ b/src/strings/astring.py
@@ -25,7 +25,7 @@ class AString(object):
test_extra = '''
using tmwa::operator "" _s;
- #include "../src/strings/zstring.hpp"
+ #include "../strings/zstring.hpp"
'''
tests = [
diff --git a/src/strings/fwd.hpp b/src/strings/fwd.hpp
index b1b8266..29762f9 100644
--- a/src/strings/fwd.hpp
+++ b/src/strings/fwd.hpp
@@ -23,6 +23,8 @@
#include <cstddef>
#include <cstdint>
+// strings/fwd.hpp is rank 1
+
namespace tmwa
{
diff --git a/src/strings/rstring.cpp b/src/strings/rstring.cpp
index e74d1d5..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)
{
}
@@ -58,13 +58,18 @@ namespace strings
}
RString& RString::operator = (const RString& r)
{
- // order important for self-assign
- if (!r.maybe_end)
- r.u.owned->count++;
- if (!maybe_end && !u.owned->count--)
- ::operator delete(u.owned);
- u = r.u;
- maybe_end = r.maybe_end;
+ // this turns out to be a win
+ // certain callers end up needing to do self-assignment a *lot*,
+ // leading to pointless ++,--s
+ if (this->u.owned != r.u.owned)
+ {
+ if (!r.maybe_end)
+ r.u.owned->count++;
+ if (!maybe_end && !u.owned->count--)
+ ::operator delete(u.owned);
+ u = r.u;
+ maybe_end = r.maybe_end;
+ }
return *this;
}
RString& RString::operator = (RString&& r)
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&&);
diff --git a/src/strings/rstring.py b/src/strings/rstring.py
index 61603d8..75fe2db 100644
--- a/src/strings/rstring.py
+++ b/src/strings/rstring.py
@@ -1,3 +1,6 @@
+# used by other pretty-printers
+rstring_disable_children = False
+
class RString(object):
__slots__ = ('_value')
name = 'tmwa::strings::RString'
@@ -21,6 +24,8 @@ class RString(object):
return b.lazy_string(length=d)
def children(self):
+ if rstring_disable_children:
+ return
v = self._value
if v['maybe_end']:
pass
@@ -31,7 +36,7 @@ class RString(object):
test_extra = '''
using tmwa::operator "" _s;
- #include "../src/strings/zstring.hpp"
+ #include "../strings/zstring.hpp"
'''
tests = [
diff --git a/src/strings/strings2_test.cpp b/src/strings/strings2_test.cpp
index 8ac8482..8b91306 100644
--- a/src/strings/strings2_test.cpp
+++ b/src/strings/strings2_test.cpp
@@ -228,4 +228,13 @@ TEST(StringTests, rlong)
EXPECT_EQ(&*r.begin(), &*r3.begin());
EXPECT_EQ(&*a.begin(), &*a3.begin());
}
+
+TEST(StringTest, rself)
+{
+ // force dynamic allocation; valgrind will check for memory errors
+ RString r = XString("foo bar baz"_s);
+ RString r2 = r;
+ r = r;
+ r = r2;
+}
} // namespace tmwa
diff --git a/src/strings/vstring.cpp b/src/strings/vstring.cpp
deleted file mode 100644
index 1cb313a..0000000
--- a/src/strings/vstring.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "vstring.hpp"
-// strings/vstring.cpp - Functions for vstring.hpp
-//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-namespace strings
-{
-} // namespace strings
-} // namespace tmwa
diff --git a/src/strings/xstring.py b/src/strings/xstring.py
index b2e33bb..ae764df 100644
--- a/src/strings/xstring.py
+++ b/src/strings/xstring.py
@@ -22,10 +22,10 @@ class XString(object):
'''
tests = [
- ('tmwa::XString(""_s)', '"" = {base = 0x0}'),
- ('tmwa::XString("Hello"_s)', '"Hello" = {base = 0x0}'),
- ('tmwa::XString("' + str256[:-2] + '"_s)', '"' + str256[:-2] + '" = {base = 0x0}'),
- ('tmwa::XString("' + str256[:-1] + '"_s)', '"' + str256[:-1] + '" = {base = 0x0}'),
- ('tmwa::XString("' + str256 + '"_s)', '"' + str256 + '" = {base = 0x0}'),
- ('tmwa::XString("' + str256 + 'x"_s)', '"' + str256 + 'x" = {base = 0x0}'),
+ ('tmwa::XString(""_s)', '"" = {base = nullptr}'),
+ ('tmwa::XString("Hello"_s)', '"Hello" = {base = nullptr}'),
+ ('tmwa::XString("' + str256[:-2] + '"_s)', '"' + str256[:-2] + '" = {base = nullptr}'),
+ ('tmwa::XString("' + str256[:-1] + '"_s)', '"' + str256[:-1] + '" = {base = nullptr}'),
+ ('tmwa::XString("' + str256 + '"_s)', '"' + str256 + '" = {base = nullptr}'),
+ ('tmwa::XString("' + str256 + 'x"_s)', '"' + str256 + 'x" = {base = nullptr}'),
]
diff --git a/src/strings/zstring.py b/src/strings/zstring.py
index f57252f..570c8f1 100644
--- a/src/strings/zstring.py
+++ b/src/strings/zstring.py
@@ -22,10 +22,10 @@ class ZString(object):
'''
tests = [
- ('tmwa::ZString(""_s)', '"" = {base = 0x0}'),
- ('tmwa::ZString("Hello"_s)', '"Hello" = {base = 0x0}'),
- ('tmwa::ZString("' + str256[:-2] + '"_s)', '"' + str256[:-2] + '" = {base = 0x0}'),
- ('tmwa::ZString("' + str256[:-1] + '"_s)', '"' + str256[:-1] + '" = {base = 0x0}'),
- ('tmwa::ZString("' + str256 + '"_s)', '"' + str256 + '" = {base = 0x0}'),
- ('tmwa::ZString("' + str256 + 'x"_s)', '"' + str256 + 'x" = {base = 0x0}'),
+ ('tmwa::ZString(""_s)', '"" = {base = nullptr}'),
+ ('tmwa::ZString("Hello"_s)', '"Hello" = {base = nullptr}'),
+ ('tmwa::ZString("' + str256[:-2] + '"_s)', '"' + str256[:-2] + '" = {base = nullptr}'),
+ ('tmwa::ZString("' + str256[:-1] + '"_s)', '"' + str256[:-1] + '" = {base = nullptr}'),
+ ('tmwa::ZString("' + str256 + '"_s)', '"' + str256 + '" = {base = nullptr}'),
+ ('tmwa::ZString("' + str256 + 'x"_s)', '"' + str256 + 'x" = {base = nullptr}'),
]
diff --git a/src/tests/fdhack.cpp b/src/tests/fdhack.cpp
deleted file mode 100644
index 7a95431..0000000
--- a/src/tests/fdhack.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "fdhack.hpp"
-// fdhack.cpp - Move file descriptors around.
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// 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 "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/tests/fwd.hpp b/src/tests/fwd.hpp
index 48627da..3ee52ff 100644
--- a/src/tests/fwd.hpp
+++ b/src/tests/fwd.hpp
@@ -20,6 +20,10 @@
#include "../sanity.hpp"
+#include "../strings/fwd.hpp" // rank 1
+#include "../io/fwd.hpp" // rank 4
+// tests/fwd.hpp is rank 5, but gtests do not require rank
+
namespace tmwa
{
diff --git a/src/warnings.hpp b/src/warnings.hpp
index 9389766..884857f 100644
--- a/src/warnings.hpp
+++ b/src/warnings.hpp
@@ -161,7 +161,10 @@ DIAG_E(trigraphs);
DIAG_E(type_limits);
DIAG_E(undef);
DIAG_E(uninitialized);
+// clang bug, fixed in 3.5
+#ifndef GENERATING_DEPENDENCIES
DIAG_E(unknown_pragmas);
+#endif
DIAG_W(unreachable_code);
DIAG_X(unsafe_loop_optimizations);
DIAG_E(unused_but_set_parameter);
diff --git a/src/generic/enum.cpp b/src/wire/fwd.hpp
index 49402e9..83d5b20 100644
--- a/src/generic/enum.cpp
+++ b/src/wire/fwd.hpp
@@ -1,5 +1,5 @@
-#include "enum.hpp"
-// enum.cpp - Safe building blocks for enumerated types.
+#pragma once
+// wire/fwd.hpp - list of type names for network packets
//
// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
//
@@ -18,9 +18,17 @@
// 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 "../poison.hpp"
+#include "../sanity.hpp"
+#include "../ints/fwd.hpp" // rank 1
+#include "../compat/fwd.hpp" // rank 2
+#include "../io/fwd.hpp" // rank 4
+#include "../net/fwd.hpp" // rank 5
+#include "../proto2/fwd.hpp" // rank 8
+// wire/fwd.hpp is rank 9
namespace tmwa
{
+enum class RecvResult;
+// meh, add more when I feel like it
} // namespace tmwa
diff --git a/src/net/packets.cpp b/src/wire/packets.cpp
index 3cba856..be06283 100644
--- a/src/net/packets.cpp
+++ b/src/wire/packets.cpp
@@ -65,29 +65,29 @@ bool packet_send(Session *s, const Byte *data, size_t sz)
return true;
}
-void packet_dump(io::WriteFile& logfp, Session *s)
+void packet_dump(Session *s)
{
- FPRINTF(logfp,
+ FPRINTF(stderr,
"---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F\n"_fmt);
char tmpstr[16 + 1] {};
int i;
for (i = 0; i < packet_avail(s); i++)
{
if ((i & 15) == 0)
- FPRINTF(logfp, "%04X "_fmt, i);
+ FPRINTF(stderr, "%04X "_fmt, i);
Byte rfifob_ib;
packet_fetch(s, i, &rfifob_ib, 1);
uint8_t rfifob_i = rfifob_ib.value;
- FPRINTF(logfp, "%02x "_fmt, rfifob_i);
+ FPRINTF(stderr, "%02x "_fmt, rfifob_i);
if (rfifob_i > 0x1f)
tmpstr[i % 16] = rfifob_i;
else
tmpstr[i % 16] = '.';
if ((i - 7) % 16 == 0) // -8 + 1
- FPRINTF(logfp, " "_fmt);
+ FPRINTF(stderr, " "_fmt);
else if ((i + 1) % 16 == 0)
{
- FPRINTF(logfp, " %s\n"_fmt, tmpstr);
+ FPRINTF(stderr, " %s\n"_fmt, tmpstr);
std::fill(tmpstr + 0, tmpstr + 17, '\0');
}
}
@@ -95,12 +95,12 @@ void packet_dump(io::WriteFile& logfp, Session *s)
{
for (int j = i; j % 16 != 0; j++)
{
- FPRINTF(logfp, " "_fmt);
+ FPRINTF(stderr, " "_fmt);
if ((j - 7) % 16 == 0) // -8 + 1
- FPRINTF(logfp, " "_fmt);
+ FPRINTF(stderr, " "_fmt);
}
- FPRINTF(logfp, " %s\n"_fmt, tmpstr);
+ FPRINTF(stderr, " %s\n"_fmt, tmpstr);
}
- FPRINTF(logfp, "\n"_fmt);
+ FPRINTF(stderr, "\n"_fmt);
}
} // namespace tmwa
diff --git a/src/net/packets.hpp b/src/wire/packets.hpp
index 5cc377c..82cc919 100644
--- a/src/net/packets.hpp
+++ b/src/wire/packets.hpp
@@ -22,16 +22,13 @@
#include <vector>
-#include "../compat/cast.hpp"
-
#include "../ints/little.hpp"
-#include "../io/fwd.hpp"
+#include "../compat/cast.hpp"
-// TODO ordering violation, should invert
#include "../proto2/fwd.hpp"
-#include "socket.hpp"
+#include "../net/socket.hpp"
namespace tmwa
@@ -56,7 +53,7 @@ enum class SendResult
size_t packet_avail(Session *s);
-void packet_dump(io::WriteFile& out, Session *s);
+void packet_dump(Session *s);
bool packet_fetch(Session *s, size_t offset, Byte *data, size_t sz);
void packet_discard(Session *s, size_t sz);