summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG37
-rw-r--r--Makefile.in2
-rw-r--r--README.md12
-rw-r--r--src/ast/npc.cpp55
-rw-r--r--src/ast/npc.hpp11
-rw-r--r--src/ast/npc_test.cpp68
-rw-r--r--src/ast/quest.cpp125
-rw-r--r--src/ast/quest.hpp65
-rw-r--r--src/char/char.cpp49
-rw-r--r--src/login/login.cpp87
-rw-r--r--src/map/atcommand.cpp110
-rw-r--r--src/map/chrif.cpp147
-rw-r--r--src/map/clif.cpp64
-rw-r--r--src/map/clif.hpp4
-rw-r--r--src/map/fwd.hpp1
-rw-r--r--src/map/globals.cpp2
-rw-r--r--src/map/globals.hpp1
-rw-r--r--src/map/magic-stmt.cpp2
-rw-r--r--src/map/map.cpp3
-rw-r--r--src/map/mob.cpp50
-rw-r--r--src/map/npc-parse.cpp124
-rw-r--r--src/map/npc.cpp2
-rw-r--r--src/map/pc.cpp96
-rw-r--r--src/map/pc.hpp3
-rw-r--r--src/map/quest.cpp135
-rw-r--r--src/map/quest.hpp (renamed from src/monitor/globals.hpp)34
-rw-r--r--src/map/script-call.cpp4
-rw-r--r--src/map/script-fun.cpp163
-rw-r--r--src/mmo/fwd.hpp1
-rw-r--r--src/mmo/ids.hpp1
-rw-r--r--src/mmo/ids.py1
-rw-r--r--src/monitor/fwd.hpp37
-rw-r--r--src/monitor/globals.cpp33
-rw-r--r--src/monitor/main.cpp243
-rwxr-xr-xtools/config.py8
-rwxr-xr-xtools/protocol.py32
36 files changed, 820 insertions, 992 deletions
diff --git a/CHANGELOG b/CHANGELOG
index cc2b10c..79900d6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,40 @@
+v15.5.04
+ - remove check for pk_mode for mapflag NOPVP
+ - remove atcommand gm
+ - add atcommand npc
+ - remove builtin readparam
+ - remove builtin statusup2
+ - remove builtin changesex
+ - remove builtin gmcommand
+ - add builtin wgm
+ - add builtin gmlog
+ - do not force disconnection after sex change
+ - add new parameter to builtin emotion
+ - remove braces and jname from getitemlink
+ - add quest log
+ - add sanity check to freeloop
+ - add OnPCLoginEvent handler
+ - some typo fixes and rewording
+v15.4.20
+ - make spam warnings appear in General chat tab
+ - add builtin getnpcx
+ - add builtin getnpcy
+ - add builtin pvp
+ - add builtin getpvpflag
+ - add builtin strnpcinfo
+ - remove builtin cmdothernpc
+ - remove builtin killmonsterall
+ - remove builtin percentheal
+ - remove builtin itemheal
+ - modify builtin heal to include itemheal
+ - remove atcommand killer
+ - remove atcommand killable
+ - remove atcommand charkiller
+ - add atcommand pvp
+ - add atcommand charpvp
+ - add atcommand exprate
+ - remove warp debug (npc 722)
+ - add support for emote IDs over 100
v15.2.28
- Added Freeloop
v15.1.23:
diff --git a/Makefile.in b/Makefile.in
index 32dc73b..006594a 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -767,4 +767,4 @@ doc: ${DOC_PNGS}
most: $(filter-out bin/${tmwa}-map.elf,${BINARIES})
magic: $(filter obj/map/magic%,${PDC_OBJECTS})
-common: $(filter-out %/lib.pdc.o obj/debug-debug/% %_test.pdc.o obj/login/% obj/char/% obj/map/% obj/admin/% obj/monitor/%,${PDC_OBJECTS})
+common: $(filter-out %/lib.pdc.o obj/debug-debug/% %_test.pdc.o obj/login/% obj/char/% obj/map/% obj/admin/%,${PDC_OBJECTS})
diff --git a/README.md b/README.md
index a48e8de..309a1d6 100644
--- a/README.md
+++ b/README.md
@@ -151,19 +151,19 @@ to installed files has begun.
####tmwa-monitor:
+<DEPRECATED>
Formerly known as `eathena-monitor`.
An unmaintained tool whose job was to keep restarting the servers
every time they crashed. It still builds in case anyone was using it,
but it proved inflexible and has't really been kept up-to-date with our
(TMW's) server-data, and besides, the server doesn't crash much now.
+There are also a number of other Open Source programs that monitor
+services already.
-At some point I plan to rewrite it and ship a new conf file, unless
-everyone agrees to use systemd, in which case I maybe can use that.
-
-In the mean time, there is a `run-all` script in the server-data repo
-that starts the appropriate server for that config. On the main server,
-we instead start the servers (and bots) individually in a tmux.
+There is a `run-all` script in the server-data repo that starts the
+appropriate server for that config. On the main server, we instead
+start the servers (and bots) individually in a tmux.
####tmwa-admin:
Formerly known as `ladmin` ("local").
diff --git a/src/ast/npc.cpp b/src/ast/npc.cpp
index e9c3464..8d4a43e 100644
--- a/src/ast/npc.cpp
+++ b/src/ast/npc.cpp
@@ -306,7 +306,7 @@ namespace npc
static
Result<ScriptNone> parse_script_none_head(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
{
- // ScriptNone: -|script|script name|-1{code}
+ // ScriptNone: -|script|script name|32767{code}
if (bits.size() != 4)
{
return Err(span.error_str("expect 4 |component|s"_s));
@@ -319,10 +319,10 @@ namespace npc
{
return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
- assert(bits[3].data[0].data == "-1"_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 -1"_s));
+ return Err(bits[3].span.error_str("in |component 4| should be just 32767"_s));
}
ScriptNone script_none;
@@ -333,39 +333,6 @@ namespace npc
return Ok(std::move(script_none));
}
static
- Result<ScriptMapNone> parse_script_map_none_head(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
- {
- // ScriptMapNone: m,x,y,d|script|script name|-1{code}
- if (bits.size() != 4)
- {
- 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[0].data != "-1"_s)
- {
- return Err(bits[3].span.error_str("in |component 4| should be just -1"_s));
- }
-
- ScriptMapNone script_map_none;
- TRY_EXTRACT(bits[0].data[0], script_map_none.m);
- TRY_EXTRACT(bits[0].data[1], script_map_none.x);
- TRY_EXTRACT(bits[0].data[2], script_map_none.y);
- TRY_EXTRACT(bits[0].data[3], script_map_none.d);
- TRY_EXTRACT(bits[2].data[0], script_map_none.name);
- script_map_none.key4_span = bits[3].data[0].span;
- // also expect '{' and parse real script (in caller)
- return Ok(std::move(script_map_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}
@@ -417,10 +384,9 @@ namespace npc
static
Result<Script> parse_script_any(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
{
- // 4 cases:
+ // 3 cases:
// ScriptFunction: function|script|Fun Name{code}
- // ScriptNone: -|script|script name|-1{code}
- // ScriptMapNone: m,x,y,d|script|script name|-1{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)
{
@@ -445,17 +411,6 @@ namespace npc
rv.body = TRY(ast::script::parse_script_body(lr, opt));
return Ok(std::move(rv));
}
- else if (bits.size() >= 4 && bits[3].data[0].data == "-1"_s)
- {
- Script rv = TRY(parse_script_map_none_head(span, bits));
- rv.key_span = bits[1].data[0].span;
-
- ast::script::ScriptOptions opt;
- opt.implicit_start = true;
- opt.no_start = true;
- rv.body = TRY(ast::script::parse_script_body(lr, opt));
- return Ok(std::move(rv));
- }
else
{
ScriptMap script_map = TRY(parse_script_map_head(span, bits));
diff --git a/src/ast/npc.hpp b/src/ast/npc.hpp
index a51acd3..ca6479e 100644
--- a/src/ast/npc.hpp
+++ b/src/ast/npc.hpp
@@ -103,14 +103,6 @@ namespace npc
Spanned<NpcName> name;
io::LineSpan key4_span;
};
- struct ScriptMapNone
- {
- Spanned<MapName> m;
- Spanned<unsigned> x, y;
- Spanned<DIR> d;
- Spanned<NpcName> name;
- io::LineSpan key4_span;
- };
struct ScriptMap
{
Spanned<MapName> m;
@@ -120,13 +112,12 @@ namespace npc
Spanned<Species> npc_class;
Spanned<unsigned> xs, ys;
};
- using ScriptBase = Variant<ScriptFunction, ScriptNone, ScriptMapNone, ScriptMap>;
+ 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(ScriptMapNone s) : ScriptBase(std::move(s)) {}
Script(ScriptMap s) : ScriptBase(std::move(s)) {}
io::LineSpan key_span;
diff --git a/src/ast/npc_test.cpp b/src/ast/npc_test.cpp
index a753623..dadeba7 100644
--- a/src/ast/npc_test.cpp
+++ b/src/ast/npc_test.cpp
@@ -423,11 +423,11 @@ namespace npc
{
// 1 2 3
//23456789012345678901234567890123456789
- "-|script|#config|-1{end;}"_s,
+ "-|script|#config|32767{end;}"_s,
// 123456
- "-|script|#config|-1\n{end;}\n"_s,
+ "-|script|#config|32767\n{end;}\n"_s,
// 1234567
- "-|script|#config|-1\n \n {end;} "_s,
+ "-|script|#config|32767\n \n {end;} "_s,
};
for (auto input : inputs)
{
@@ -435,7 +435,7 @@ namespace npc
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,19);
+ EXPECT_SPAN(top.span, 1,1, 1,22);
auto script = top.get_if<Script>();
EXPECT_TRUE(script);
auto p = script->get_if<ScriptNone>();
@@ -446,66 +446,10 @@ namespace npc
EXPECT_SPAN(script->key_span, 1,3, 1,8);
EXPECT_SPAN(p->name.span, 1,10, 1,16);
EXPECT_EQ(p->name.data, stringish<NpcName>("#config"_s));
- EXPECT_SPAN(p->key4_span, 1,18, 1,19);
+ EXPECT_SPAN(p->key4_span, 1,18, 1,22);
if (input.endswith('}'))
{
- EXPECT_SPAN(script->body.span, 1,20, 1,25);
- }
- 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, scriptmapnone)
- {
- QuietFd q;
- LString inputs[] =
- {
- // 1 2 3
- //23456789012345678901234567890123456789
- "map.gat,1,2,3|script|Init|-1{end;}"_s,
- "map.gat,1,2,3|script|Init|-1\n{end;}\n"_s,
- "map.gat,1,2,3|script|Init|-1\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,28);
- auto script = top.get_if<Script>();
- EXPECT_TRUE(script);
- auto p = script->get_if<ScriptMapNone>();
- 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(script->key_span, 1,15, 1,20);
- EXPECT_SPAN(p->name.span, 1,22, 1,25);
- EXPECT_EQ(p->name.data, stringish<NpcName>("Init"_s));
- EXPECT_SPAN(p->key4_span, 1,27, 1,28);
- if (input.endswith('}'))
- {
- EXPECT_SPAN(script->body.span, 1,29, 1,34);
+ EXPECT_SPAN(script->body.span, 1,23, 1,28);
}
else if (input.endswith('\n'))
{
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/char/char.cpp b/src/char/char.cpp
index a9b6834..ed9e369 100644
--- a/src/char/char.cpp
+++ b/src/char/char.cpp
@@ -1223,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;
@@ -1857,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:
{
diff --git a/src/login/login.cpp b/src/login/login.cpp
index 66b3ea0..92f8cc0 100644
--- a/src/login/login.cpp
+++ b/src/login/login.cpp
@@ -982,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 == login_conf.gm_pass)
- {
- // only non-GM can become GM
- if (!isGM(acc))
- {
- // if we autorise creation
- if (login_conf.level_new_gm)
- {
- // if we can open the file to add the new GM
- io::AppendFile fp(login_conf.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, login_conf.level_new_gm);
- if (!fp.close())
- {
- PRINTF("warning: didn't actually save GM file\n"_fmt);
- }
- fixed_21.gm_level = login_conf.level_new_gm;
- read_gm_account();
- send_GM_accounts();
- PRINTF("GM Change of the account %d: level 0 -> %d.\n"_fmt,
- acc, login_conf.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,
- login_conf.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
{
diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp
index f226706..7739966 100644
--- a/src/map/atcommand.cpp
+++ b/src/map/atcommand.cpp
@@ -701,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)
{
@@ -1528,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)
{
@@ -1586,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)
{
@@ -4907,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}},
@@ -4976,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}},
diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp
index bf4ae4e..2606911 100644
--- a/src/map/chrif.cpp
+++ b/src/map/chrif.cpp
@@ -218,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;
}
@@ -241,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);
@@ -249,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;
}
@@ -266,7 +261,7 @@ 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);
}
@@ -345,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
*------------------------------------------
*/
@@ -405,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);
}
@@ -419,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
@@ -436,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
{
@@ -445,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;
}
@@ -466,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;
}
@@ -487,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;
}
@@ -508,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;
}
@@ -529,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;
}
@@ -553,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)
*------------------------------------------
*/
@@ -609,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
@@ -746,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;
@@ -783,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,
@@ -795,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,
@@ -803,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,
@@ -823,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;
}
}
@@ -842,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;
@@ -855,7 +808,7 @@ 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;
@@ -865,7 +818,7 @@ 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;
}
@@ -973,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;
@@ -1064,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;
@@ -1118,7 +1061,7 @@ 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(map_conf.char_ip, map_conf.char_port,
SessionParsers{.func_parse= chrif_parse, .func_delete= chrif_delete});
diff --git a/src/map/clif.cpp b/src/map/clif.cpp
index 577d7be..2c65d44 100644
--- a/src/map/clif.cpp
+++ b/src/map/clif.cpp
@@ -198,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,
@@ -906,7 +901,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;
@@ -2325,7 +2320,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;
@@ -3113,7 +3108,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)
{
@@ -4671,6 +4665,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
diff --git a/src/map/clif.hpp b/src/map/clif.hpp
index 153cc7c..5117ff3 100644
--- a/src/map/clif.hpp
+++ b/src/map/clif.hpp
@@ -96,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
@@ -174,6 +175,9 @@ 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
diff --git a/src/map/fwd.hpp b/src/map/fwd.hpp
index 1db4ed0..911d566 100644
--- a/src/map/fwd.hpp
+++ b/src/map/fwd.hpp
@@ -65,6 +65,7 @@ class npc_data_warp;
class npc_data_message;
struct item_data;
+struct quest_data;
struct ScriptState;
struct str_data_t;
diff --git a/src/map/globals.cpp b/src/map/globals.cpp
index 09ff157..dce3906 100644
--- a/src/map/globals.cpp
+++ b/src/map/globals.cpp
@@ -26,6 +26,7 @@
#include "battle_conf.hpp"
#include "itemdb.hpp"
+#include "quest.hpp"
#include "magic-interpreter.hpp"
#include "map_conf.hpp"
#include "mob.hpp"
@@ -49,6 +50,7 @@ namespace tmwa
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
diff --git a/src/map/globals.hpp b/src/map/globals.hpp
index 33cfec8..b457b4e 100644
--- a/src/map/globals.hpp
+++ b/src/map/globals.hpp
@@ -48,6 +48,7 @@ namespace tmwa
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
diff --git a/src/map/magic-stmt.cpp b/src/map/magic-stmt.cpp
index fdeac3a..4d8330a 100644
--- a/src/map/magic-stmt.cpp
+++ b/src/map/magic-stmt.cpp
@@ -773,7 +773,7 @@ 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)
- && (caster->bl_type != BL::PC)
+ && (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 */
diff --git a/src/map/map.cpp b/src/map/map.cpp
index d502fbb..c1d760a 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -74,6 +74,7 @@
#include "magic-v2.hpp"
#include "map_conf.hpp"
#include "mob.hpp"
+#include "quest.hpp"
#include "npc.hpp"
#include "npc-parse.hpp"
#include "party.hpp"
@@ -1489,6 +1490,8 @@ bool map_confs(io::Spanned<XString> key, io::Spanned<ZString> value)
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)
diff --git a/src/map/mob.cpp b/src/map/mob.cpp
index cdb348c..539b547 100644
--- a/src/map/mob.cpp
+++ b/src/map/mob.cpp
@@ -333,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);
@@ -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++)
{
diff --git a/src/map/npc-parse.cpp b/src/map/npc-parse.cpp
index 443a1e7..4d9fcbd 100644
--- a/src/map/npc-parse.cpp
+++ b/src/map/npc-parse.cpp
@@ -339,7 +339,7 @@ bool npc_load_mapflag(ast::npc::MapFlag& mapflag)
return false;
}
- if (battle_config.pk_mode && mf == MapFlag::NOPVP)
+ if (mf == MapFlag::NOPVP)
{
if (mapflag.vec_extra.data.size())
{
@@ -446,7 +446,7 @@ bool npc_load_script_none(ast::script::ScriptBody& body, ast::npc::ScriptNone& s
nd->bl_id = npc_get_new_npc_id();
nd->dir = DIR::S;
nd->flag = 0;
- nd->npc_class = NEGATIVE_SPECIES;
+ nd->npc_class = INVISIBLE_CLASS;
nd->speed = 200_ms;
nd->scr.script = std::move(script);
nd->option = Opt0::ZERO;
@@ -514,115 +514,6 @@ bool npc_load_script_none(ast::script::ScriptBody& body, ast::npc::ScriptNone& s
}
static
-bool npc_load_script_map_none(ast::script::ScriptBody& body, ast::npc::ScriptMapNone& script_map_none)
-{
- MapName mapname = script_map_none.m.data;
- int x = script_map_none.x.data, y = script_map_none.y.data;
- DIR dir = script_map_none.d.data;
- P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname),
- {
- script_map_none.m.span.error("No such map"_s);
- return false;
- });
-
- std::unique_ptr<const ScriptBuffer> script = compile_script(STRPRINTF("script npc \"%s\""_fmt, script_map_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_map_none.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 = NEGATIVE_SPECIES;
- nd->speed = 200_ms;
- nd->scr.script = std::move(script);
- nd->option = Opt0::ZERO;
- nd->opt1 = Opt1::ZERO;
- nd->opt2 = Opt2::ZERO;
- nd->opt3 = Opt3::ZERO;
-
- npc_script++;
- nd->bl_type = BL::NPC;
- nd->npc_subtype = NpcSubtype::SCRIPT;
-
- nd->n = map_addnpc(m, nd);
- map_addblock(nd);
-
- {
- struct event_data ev {};
- ev.nd = nd;
- ev.pos = 0;
- NpcEvent npcev;
- npcev.npc = nd->name;
- npcev.label = ScriptLabel();
- ev_db.insert(npcev, ev);
- }
-
- register_npc_name(nd);
-
- for (auto& pair : scriptlabel_db)
- 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;
@@ -761,17 +652,6 @@ bool npc_load_script_any(ast::npc::Script *script)
{
return npc_load_script_none(script->body, script_none);
}
- MATCH_CASE (ast::npc::ScriptMapNone&, script_map_none)
- {
- auto& mapname = script_map_none.m;
- Option<P<map_local>> m = map_mapname2mapid(mapname.data);
- if (m.is_none())
- {
- mapname.span.error(STRPRINTF("Map not found: %s"_fmt, mapname.data));
- return false;
- }
- return npc_load_script_map_none(script->body, script_map_none);
- }
MATCH_CASE (ast::npc::ScriptMap&, script_map)
{
auto& mapname = script_map.m;
diff --git a/src/map/npc.cpp b/src/map/npc.cpp
index 56c33cc..4296432 100644
--- a/src/map/npc.cpp
+++ b/src/map/npc.cpp
@@ -583,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;
// エリア判定
diff --git a/src/map/pc.cpp b/src/map/pc.cpp
index 29be372..6fa35b0 100644
--- a/src/map/pc.cpp
+++ b/src/map/pc.cpp
@@ -64,6 +64,7 @@
#include "skill.hpp"
#include "storage.hpp"
#include "trade.hpp"
+#include "quest.hpp"
#include "../poison.hpp"
@@ -794,6 +795,9 @@ int pc_authok(AccountId id, int login_id2,
pc_calcstatus(sd, 1);
+ npc_event_doall_l(stringish<ScriptLabel>("OnPCLoginEvent"_s), sd->bl_id, nullptr);
+ // Init Quest Log
+ clif_sendallquest(sd);
return 0;
}
@@ -2854,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
@@ -2861,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;
@@ -2884,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;
@@ -3480,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;
@@ -3507,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);
@@ -3787,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;
@@ -3807,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)
@@ -3816,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)
{
@@ -3823,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;
}
}
@@ -3835,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;
}
diff --git a/src/map/pc.hpp b/src/map/pc.hpp
index 6879f79..d100938 100644
--- a/src/map/pc.hpp
+++ b/src/map/pc.hpp
@@ -28,6 +28,7 @@
#include "../mmo/clif.t.hpp"
#include "map.hpp"
+#include "quest.hpp"
namespace tmwa
@@ -145,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 );
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/monitor/globals.hpp b/src/map/quest.hpp
index aa797d3..65e6f4e 100644
--- a/src/monitor/globals.hpp
+++ b/src/map/quest.hpp
@@ -1,7 +1,7 @@
#pragma once
-// globals.hpp - Evil global variables for tmwa-monitor.
+// quest.hpp - Quest Log.
//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2015 Ed Pasek <pasekei@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -20,14 +20,32 @@
#include "fwd.hpp"
-#include <sys/types.h>
+#include "../mmo/ids.hpp"
+#include "../high/mmo.hpp"
+#include "map.hpp"
+#include "script-buffer.hpp"
namespace tmwa
{
- namespace monitor
- {
- extern MonitorConf monitor_conf;
- extern pid_t pid_login, pid_char, pid_map;
- } // namespace monitor
+namespace map
+{
+constexpr int MAX_QUEST_DB (60355+1);
+struct quest_data
+{
+ 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-call.cpp b/src/map/script-call.cpp
index f412328..c3c6aa1 100644
--- a/src/map/script-call.cpp
+++ b/src/map/script-call.cpp
@@ -757,7 +757,7 @@ void run_script_main(ScriptState *st, Borrowed<const ScriptBuffer> rootscript)
{
rerun_pos = st->scriptp.pos;
st->state = ScriptEndState::ZERO;
- if (!st->freeloop && gotocount > 0 && (--gotocount) <= 0)
+ if (st->freeloop != 1 && gotocount > 0 && (--gotocount) <= 0)
{
PRINTF("run_script: infinity loop !\n"_fmt);
st->state = ScriptEndState::END;
@@ -806,7 +806,7 @@ void run_script_main(ScriptState *st, Borrowed<const ScriptBuffer> rootscript)
st->state = ScriptEndState::END;
break;
}
- if (!st->freeloop && cmdcount > 0 && (--cmdcount) <= 0)
+ if (st->freeloop != 1 && cmdcount > 0 && (--cmdcount) <= 0)
{
PRINTF("run_script: infinity loop !\n"_fmt);
st->state = ScriptEndState::END;
diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp
index c327f26..3291cfc 100644
--- a/src/map/script-fun.cpp
+++ b/src/map/script-fun.cpp
@@ -108,7 +108,7 @@ void builtin_goto(ScriptState *st)
{
if (!AARG(0).is<ScriptDataPos>())
{
- PRINTF("script: goto: not label !\n"_fmt);
+ PRINTF("script: goto: that's not a label!\n"_fmt);
st->state = ScriptEndState::END;
return;
}
@@ -149,7 +149,7 @@ void builtin_callfunc(ScriptState *st)
}
OMATCH_CASE_NONE ()
{
- PRINTF("script:callfunc: function not found! [%s]\n"_fmt, str);
+ PRINTF("script: callfunc: function not found! [%s]\n"_fmt, str);
st->state = ScriptEndState::END;
}
}
@@ -538,7 +538,7 @@ void builtin_setarray(ScriptState *st)
if (prefix != '$' && prefix != '@')
{
- PRINTF("builtin_setarray: illegal scope !\n"_fmt);
+ PRINTF("builtin_setarray: illegal scope!\n"_fmt);
return;
}
if (prefix != '$')
@@ -569,7 +569,7 @@ void builtin_cleararray(ScriptState *st)
if (prefix != '$' && prefix != '@')
{
- PRINTF("builtin_cleararray: illegal scope !\n"_fmt);
+ PRINTF("builtin_cleararray: illegal scope!\n"_fmt);
return;
}
if (prefix != '$')
@@ -626,7 +626,7 @@ void builtin_getarraysize(ScriptState *st)
if (prefix != '$' && prefix != '@')
{
- PRINTF("builtin_copyarray: illegal scope !\n"_fmt);
+ PRINTF("builtin_copyarray: illegal scope!\n"_fmt);
return;
}
@@ -645,7 +645,7 @@ void builtin_getelementofarray(ScriptState *st)
int i = conv_num(st, &AARG(1));
if (i > 255 || i < 0)
{
- PRINTF("script: getelementofarray (operator[]): param2 illegal number %d\n"_fmt,
+ PRINTF("script: getelementofarray (operator[]): param2 illegal number: %d\n"_fmt,
i);
push_int<ScriptDataInt>(st->stack, 0);
}
@@ -657,11 +657,29 @@ void builtin_getelementofarray(ScriptState *st)
}
else
{
- PRINTF("script: getelementofarray (operator[]): param1 not name !\n"_fmt);
+ 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));
+}
+
/*==========================================
*
*------------------------------------------
@@ -717,7 +735,7 @@ void builtin_countitem(ScriptState *st)
else
{
if (battle_config.error_log)
- PRINTF("wrong item ID : countitem (%i)\n"_fmt, nameid);
+ PRINTF("wrong item ID: countitem (%i)\n"_fmt, nameid);
}
push_int<ScriptDataInt>(st->stack, count);
@@ -755,7 +773,7 @@ void builtin_checkweight(ScriptState *st)
amount = conv_num(st, &AARG(1));
if (amount <= 0 || !nameid)
{
- //if get wrong item ID or amount<=0, don't count weight of non existing items
+ //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;
}
@@ -907,7 +925,7 @@ void builtin_delitem(ScriptState *st)
if (!nameid || amount <= 0)
{
- //by Lupus. Don't run FOR if u got wrong item ID or amount<=0
+ //By Lupus. Don't run FOR if you've got the wrong item ID or amount<=0
return;
}
@@ -934,31 +952,6 @@ void builtin_delitem(ScriptState *st)
}
/*==========================================
- *キャラ関係のパラメータ取得
- *------------------------------------------
- */
-static
-void builtin_readparam(ScriptState *st)
-{
- dumb_ptr<map_session_data> sd;
-
- SP type = SP(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;
- }
-
- push_int<ScriptDataInt>(st->stack, pc_readparam(sd, type));
-
-}
-
-/*==========================================
*キャラ関係のID取得
*------------------------------------------
*/
@@ -1150,20 +1143,6 @@ void builtin_getequipname(ScriptState *st)
}
/*==========================================
- *
- *------------------------------------------
- */
-static
-void builtin_statusup2(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_statusup2(sd, type, val);
-
-}
-
-/*==========================================
* 装備品による能力値ボーナス
*------------------------------------------
*/
@@ -1955,7 +1934,7 @@ static
void builtin_debugmes(ScriptState *st)
{
RString mes = conv_str(st, &AARG(0));
- PRINTF("script debug : %d %d : %s\n"_fmt,
+ PRINTF("script debug: %d %d: '%s'\n"_fmt,
st->rid, st->oid, mes);
}
@@ -1972,20 +1951,6 @@ void builtin_resetstatus(ScriptState *st)
}
/*==========================================
- * 性別変換
- *------------------------------------------
- */
-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のアタッチ
*------------------------------------------
*/
@@ -2164,11 +2129,22 @@ void builtin_getpvpflag(ScriptState *st)
static
void builtin_emotion(ScriptState *st)
{
- int type;
- type = conv_num(st, &AARG(0));
+ 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;
- clif_emotion(map_id2bl(st->oid), type);
+ 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
@@ -2296,11 +2272,11 @@ void builtin_getitemlink(ScriptState *st)
{
OMATCH_CASE_SOME (item_data)
{
- buf = STRPRINTF("[@@%d|%s@@]"_fmt, item_data->nameid, item_data->jname);
+ buf = STRPRINTF("@@%d|@@"_fmt, item_data->nameid);
}
OMATCH_CASE_NONE ()
{
- buf = STRPRINTF("Unknown Item: %s"_fmt, name);
+ buf = "Unknown Item"_s;
}
}
OMATCH_END ();
@@ -2577,25 +2553,6 @@ void builtin_unequipbyid(ScriptState *st)
}
/*==========================================
- * 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, &AARG(0));
-
- is_atcommand(sd->sess, sd, cmd, GmLevel::from(-1U));
-
-}
-
-/*==========================================
* npcwarp [remoitnane]
* Move NPC to a new position on the same map.
*------------------------------------------
@@ -2613,7 +2570,7 @@ void builtin_npcwarp(ScriptState *st)
if (!nd)
{
- PRINTF("builtin_npcwarp: no such npc: %s\n"_fmt, npc);
+ PRINTF("builtin_npcwarp: no such npc: '%s'\n"_fmt, npc);
return;
}
@@ -2656,7 +2613,7 @@ void builtin_npcareawarp(ScriptState *st)
if (!nd)
{
- PRINTF("builtin_npcareawarp: no such npc: %s\n"_fmt, npc);
+ PRINTF("builtin_npcareawarp: no such npc: '%s'\n"_fmt, npc);
return;
}
@@ -2901,7 +2858,7 @@ void builtin_shop(ScriptState *st)
nd = npc_name2id(name);
if (!nd)
{
- PRINTF("builtin_shop: no such npc: %s\n"_fmt, name);
+ PRINTF("builtin_shop: no such npc: '%s'\n"_fmt, name);
return;
}
@@ -2934,7 +2891,7 @@ void builtin_fakenpcname(ScriptState *st)
dumb_ptr<npc_data> nd = npc_name2id(name);
if (!nd)
{
- PRINTF("builtin_fakenpcname: no such npc: %s\n"_fmt, name);
+ PRINTF("builtin_fakenpcname: no such npc: '%s'\n"_fmt, name);
return;
}
nd->name = newname;
@@ -2996,7 +2953,7 @@ void builtin_strnpcinfo(ScriptState *st)
nd = npc_name2id(npc);
if (!nd)
{
- PRINTF("builtin_strnpcinfo: no such npc: %s\n"_fmt, npc);
+ PRINTF("builtin_strnpcinfo: no such npc: '%s'\n"_fmt, npc);
return;
}
} else {
@@ -3036,7 +2993,7 @@ void builtin_getnpcx(ScriptState *st)
nd = npc_name2id(name);
if (!nd)
{
- PRINTF("builtin_getnpcx: no such npc: %s\n"_fmt, name);
+ PRINTF("builtin_getnpcx: no such npc: '%s'\n"_fmt, name);
return;
}
} else {
@@ -3060,7 +3017,7 @@ void builtin_getnpcy(ScriptState *st)
nd = npc_name2id(name);
if (!nd)
{
- PRINTF("builtin_getnpcy: no such npc: %s\n"_fmt, name);
+ PRINTF("builtin_getnpcy: no such npc: '%s'\n"_fmt, name);
return;
}
} else {
@@ -3109,12 +3066,10 @@ BuiltinFunction builtin_functions[] =
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'),
@@ -3134,11 +3089,11 @@ BuiltinFunction builtin_functions[] =
BUILTIN(killmonster, "ME"_s, '\0'),
BUILTIN(donpcevent, "E"_s, '\0'),
BUILTIN(addtimer, "tE"_s, '\0'),
- BUILTIN(initnpctimer, ""_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(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'),
@@ -3151,8 +3106,9 @@ BuiltinFunction builtin_functions[] =
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(changesex, ""_s, '\0'),
BUILTIN(attachrid, "i"_s, 'i'),
BUILTIN(detachrid, ""_s, '\0'),
BUILTIN(isloggedin, "i"_s, 'i'),
@@ -3163,7 +3119,7 @@ BuiltinFunction builtin_functions[] =
BUILTIN(pvpoff, "M"_s, '\0'),
BUILTIN(setpvpchannel, "i"_s, '\0'),
BUILTIN(getpvpflag, "i"_s, 'i'),
- BUILTIN(emotion, "i"_s, '\0'),
+ BUILTIN(emotion, "i?"_s, '\0'),
BUILTIN(mapwarp, "MMxy"_s, '\0'),
BUILTIN(mobcount, "ME"_s, 'i'),
BUILTIN(marriage, "P"_s, 'i'),
@@ -3182,7 +3138,6 @@ BuiltinFunction builtin_functions[] =
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(npcareawarp, "xyxyis"_s, '\0'),
BUILTIN(message, "Ps"_s, '\0'),
diff --git a/src/mmo/fwd.hpp b/src/mmo/fwd.hpp
index f51767d..434885e 100644
--- a/src/mmo/fwd.hpp
+++ b/src/mmo/fwd.hpp
@@ -51,6 +51,7 @@ class AccountCrypt;
class AccountEmail;
class ServerName;
class PartyName;
+class QuestId;
class VarName;
class MapName;
class CharName;
diff --git a/src/mmo/ids.hpp b/src/mmo/ids.hpp
index c1482ef..28b146a 100644
--- a/src/mmo/ids.hpp
+++ b/src/mmo/ids.hpp
@@ -40,6 +40,7 @@ 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
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/monitor/fwd.hpp b/src/monitor/fwd.hpp
deleted file mode 100644
index 6900e8e..0000000
--- a/src/monitor/fwd.hpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#pragma once
-// monitor/fwd.hpp - list of type names for monitor nonserver
-//
-// 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 "../strings/fwd.hpp" // rank 1
-#include "../io/fwd.hpp" // rank 4
-#include "../net/fwd.hpp" // rank 5
-#include "../mmo/fwd.hpp" // rank 6
-#include "../high/fwd.hpp" // rank 9
-// monitor/fwd.hpp is rank ∞ because it is an executable
-
-
-namespace tmwa
-{
-namespace monitor
-{
- struct MonitorConf;
-} // namespace monitor
-} // namespace tmwa
diff --git a/src/monitor/globals.cpp b/src/monitor/globals.cpp
deleted file mode 100644
index 49e814d..0000000
--- a/src/monitor/globals.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "globals.hpp"
-// globals.cpp - Evil global variables for tmwa-monitor.
-//
-// 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 "monitor_conf.hpp"
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
- namespace monitor
- {
- MonitorConf monitor_conf;
- pid_t pid_login, pid_char, pid_map;
- } // namespace monitor
-} // namespace tmwa
diff --git a/src/monitor/main.cpp b/src/monitor/main.cpp
deleted file mode 100644
index f21a4a7..0000000
--- a/src/monitor/main.cpp
+++ /dev/null
@@ -1,243 +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/line.hpp"
-
-#include "../mmo/config_parse.hpp"
-#include "../mmo/version.hpp"
-
-#include "../net/timestamp-utils.hpp"
-
-#include "globals.hpp"
-#include "monitor_conf.hpp"
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-namespace monitor
-{
-static
-AString make_path(XString base, XString path)
-{
- MString m;
- m += base;
- m += '/';
- m += path;
- return AString(m);
-}
-
-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_login)
- kill(pid_login, sig);
- if (pid_char)
- kill(pid_char, sig);
- if (pid_map)
- kill(pid_map, sig);
- DIAG_PUSH();
- DIAG_I(old_style_cast);
- DIAG_I(zero_as_null_pointer_constant);
- signal(sig, SIG_DFL);
- DIAG_POP();
- raise(sig);
-}
-
-static
-bool monitor_config(io::Spanned<XString> key, io::Spanned<ZString> value)
-{
- return parse_monitor_conf(monitor_conf, key, value);
-}
-
-static
-bool monitor_confs(io::Spanned<XString> key, io::Spanned<ZString> value)
-{
- if (key.data == "monitor_conf"_s)
- {
- return load_config_file(value.data, monitor_config);
- }
- key.span.error("Unknown meta-key for monitor nonserver"_s);
- return false;
-}
-} // namespace monitor
-} // namespace tmwa
-
-int main(int argc, char *argv[])
-{
- using namespace tmwa;
- using namespace tmwa::monitor;
- // 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);
-
- monitor_conf.workdir = make_path(ZString(strings::really_construct_from_a_pointer, getenv("HOME"), nullptr), "tmwserver"_s);
-
- ZString argv0 = ZString(strings::really_construct_from_a_pointer, argv[0], nullptr);
- bool loaded_config_yet = false;
- bool runflag = true;
-
- for (int ai = 1; ai < argc; ++ai)
- {
- ZString argvi = ZString(strings::really_construct_from_a_pointer, argv[ai], nullptr);
- if (argvi.startswith('-'))
- {
- if (argvi == "--help"_s)
- {
- PRINTF("Usage: %s [--help] [--version] [files...]\n"_fmt,
- argv0);
- exit(0);
- }
- else if (argvi == "--version"_s)
- {
- PRINTF("%s\n"_fmt, CURRENT_VERSION_STRING);
- exit(0);
- }
- else
- {
- FPRINTF(stderr, "Unknown argument: %s\n"_fmt, argvi);
- runflag = false;
- }
- }
- else
- {
- loaded_config_yet = true;
- runflag &= load_config_file(argvi, monitor_confs);
- }
- }
-
- if (!loaded_config_yet)
- runflag &= load_config_file("conf/tmwa-monitor.conf"_s, monitor_confs);
-
- if (!runflag)
- exit(1);
-
- if (chdir(monitor_conf.workdir.c_str()) < 0)
- {
- perror("Failed to change directory");
- exit(1);
- }
-
- PRINTF("Starting:\n"_fmt);
- PRINTF("* workdir: %s\n"_fmt, monitor_conf.workdir);
- PRINTF("* login_server: %s\n"_fmt, monitor_conf.login_server);
- PRINTF("* char_server: %s\n"_fmt, monitor_conf.char_server);
- PRINTF("* map_server: %s\n"_fmt, monitor_conf.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(monitor_conf.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(monitor_conf.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(monitor_conf.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/tools/config.py b/tools/config.py
index a32e8ca..aeb4e99 100755
--- a/tools/config.py
+++ b/tools/config.py
@@ -419,7 +419,6 @@ def build_config():
admin_realm = rv.realm('src/admin')
char_realm = rv.realm('src/char')
map_realm = rv.realm('src/map')
- monitor_realm = rv.realm('src/monitor')
# confs
login_conf = login_realm.conf()
@@ -434,8 +433,6 @@ def build_config():
map_conf = map_realm.conf()
battle_conf = map_realm.conf('battle')
- monitor_conf = monitor_realm.conf()
-
# headers
cstdint_sys = SystemHeader('cstdint')
vector_sys = SystemHeader('vector')
@@ -677,11 +674,6 @@ def build_config():
battle_conf.opt('itemheal_regeneration_factor', i32, '1')
battle_conf.opt('mob_splash_radius', i32, '-1', min='-1')
- monitor_conf.opt('login_server', RString, lit('./login-server'))
- monitor_conf.opt('char_server', RString, lit('./char-server'))
- monitor_conf.opt('map_server', RString, lit('./map-server'))
- monitor_conf.opt('workdir', RString, '{}') # initialized specially
-
return rv
def main():
diff --git a/tools/protocol.py b/tools/protocol.py
index 5d4dc37..db14f6d 100755
--- a/tools/protocol.py
+++ b/tools/protocol.py
@@ -4617,7 +4617,39 @@ def build_context():
)
# 0x0213 define='CMSG_SET_STATUS',
# 0x0214 define='SMSG_QUEST_SET_VAR',
+ map_user.s(0x0214, 'send quest',
+ define='SMSG_QUEST_SET_VAR',
+ fixed=[
+ at(0, u16, 'packet id'),
+ at(2, u16, 'variable'),
+ at(4, u32, 'value'),
+ ],
+ fixed_size=8,
+ pre=[NOTHING],
+ post=[PRETTY],
+ desc='''
+ Set Quest Log Variable to Value.
+ ''',
+ )
# 0x0215 define='SMSG_QUEST_PLAYER_VARS',
+ map_user.s(0x0215, 'send all quest',
+ define='SMSG_QUEST_PLAYER_VARS',
+ head=[
+ at(0, u16, 'packet id'),
+ at(2, u16, 'packet length'),
+ ],
+ head_size=4,
+ repeat=[
+ at(0, u16, 'variable'),
+ at(2, u32, 'value'),
+ ],
+ repeat_size=6,
+ pre=[NOTHING],
+ post=[PRETTY],
+ desc='''
+ Set All Quest Log Variable to Value.
+ ''',
+ )
# 0x0220 define='SMSG_BEING_NAME_RESPONSE2',
# 0x0221 define='SMSG_CHAR_CREATE_SUCCEEDED2',
# 0x0222 define='CMSG_CHAT_MESSAGE2',