summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.cpp88
-rw-r--r--src/map/chrif.cpp58
-rw-r--r--src/map/clif.cpp60
-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/map.cpp3
-rw-r--r--src/map/npc-parse.cpp2
-rw-r--r--src/map/pc.cpp81
-rw-r--r--src/map/pc.hpp3
-rw-r--r--src/map/quest.cpp135
-rw-r--r--src/map/quest.hpp51
-rw-r--r--src/map/script-call.cpp4
-rw-r--r--src/map/script-fun.cpp78
-rw-r--r--src/mmo/fwd.hpp1
-rw-r--r--src/mmo/ids.hpp1
-rw-r--r--src/mmo/ids.py1
-rwxr-xr-xtools/protocol.py32
23 files changed, 659 insertions, 273 deletions
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 9a35d30..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)
{
@@ -4926,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}},
@@ -4995,9 +5042,6 @@ 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}},
diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp
index bf4ae4e..3ffeaf2 100644
--- a/src/map/chrif.cpp
+++ b/src/map/chrif.cpp
@@ -345,23 +345,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
*------------------------------------------
*/
@@ -559,30 +542,6 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed)
}
/*==========================================
- * 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 +568,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
@@ -973,16 +931,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;
diff --git a/src/map/clif.cpp b/src/map/clif.cpp
index 577d7be..e3cd55f 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,
@@ -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/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/npc-parse.cpp b/src/map/npc-parse.cpp
index 443a1e7..9c9a22c 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())
{
diff --git a/src/map/pc.cpp b/src/map/pc.cpp
index 00acadf..e8e526a 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;
}
@@ -3520,8 +3524,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;
@@ -3827,14 +3830,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;
@@ -3847,8 +3873,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)
@@ -3856,6 +3887,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)
{
@@ -3863,9 +3905,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;
}
}
@@ -3875,14 +3926,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 14829d1..6c0803f 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
@@ -146,6 +147,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/map/quest.hpp b/src/map/quest.hpp
new file mode 100644
index 0000000..65e6f4e
--- /dev/null
+++ b/src/map/quest.hpp
@@ -0,0 +1,51 @@
+#pragma once
+// quest.hpp - 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 "fwd.hpp"
+
+#include "../mmo/ids.hpp"
+#include "../high/mmo.hpp"
+
+#include "map.hpp"
+#include "script-buffer.hpp"
+
+namespace tmwa
+{
+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 a7f9fcc..010b095 100644
--- a/src/map/script-fun.cpp
+++ b/src/map/script-fun.cpp
@@ -677,6 +677,24 @@ void builtin_getelementofarray(ScriptState *st)
}
}
+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));
+}
+
/*==========================================
*
*------------------------------------------
@@ -1948,20 +1966,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のアタッチ
*------------------------------------------
*/
@@ -2140,11 +2144,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
@@ -2272,11 +2287,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 ();
@@ -2553,25 +2568,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.
*------------------------------------------
@@ -3125,8 +3121,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'),
@@ -3137,7 +3134,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'),
@@ -3156,7 +3153,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/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',