From 950bc8a1a239829864600b82e40d801514fca709 Mon Sep 17 00:00:00 2001 From: wushin Date: Sun, 5 Apr 2015 23:11:16 -0500 Subject: Add Bitmasked Quest Log --- src/ast/quest.cpp | 125 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ast/quest.hpp | 65 +++++++++++++++++++++++++ src/map/clif.cpp | 54 +++++++++++++++++++++ src/map/clif.hpp | 3 ++ src/map/fwd.hpp | 1 + src/map/globals.cpp | 2 + src/map/globals.hpp | 1 + src/map/map.cpp | 3 ++ src/map/pc.cpp | 78 ++++++++++++++++++++++++++---- src/map/pc.hpp | 3 ++ src/map/quest.cpp | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/map/quest.hpp | 51 ++++++++++++++++++++ src/mmo/fwd.hpp | 1 + src/mmo/ids.hpp | 1 + src/mmo/ids.py | 1 + 15 files changed, 515 insertions(+), 9 deletions(-) create mode 100644 src/ast/quest.cpp create mode 100644 src/ast/quest.hpp create mode 100644 src/map/quest.cpp create mode 100644 src/map/quest.hpp (limited to 'src') 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 +// +// 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 . + +#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> 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> parse_quest(io::LineCharReader& lr) + { + Spanned 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 +// +// 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 . + +#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; + Spanned quest_var; + Spanned quest_vr; + Spanned quest_shift; + Spanned quest_mask; + }; + + using QuestOrCommentBase = Variant; + struct QuestOrComment : QuestOrCommentBase + { + QuestOrComment(Comment o) : QuestOrCommentBase(std::move(o)) {} + QuestOrComment(Quest o) : QuestOrCommentBase(std::move(o)) {} + io::LineSpan span; + }; + + Option> parse_quest(io::LineCharReader& lr); +} // namespace quest +} // namespace ast +} // namespace tmwa diff --git a/src/map/clif.cpp b/src/map/clif.cpp index 577d7be..6a7f0ea 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -4671,6 +4671,60 @@ RecvResult clif_parse_PartyMessage(Session *s, dumb_ptr sd) return rv; } +void clif_sendallquest(dumb_ptr sd) +{ + int i; + QuestId questid; + if (!sd) + return; + + if (!sd->sess) + return; + + Session *s = sd->sess; + Packet_Head<0x0215> head_215; + std::vector> repeat_215; + + assert (sd->status.global_reg_num < GLOBAL_REG_NUM); + for (QuestId q = wrap(0); q < wrap(-1); q = next(q)) + { + P 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(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 sd, QuestId questid, int value) +{ + if (!sd) + return; + + if (!sd->sess) + return; + + Session *s = sd->sess; + + Packet_Fixed<0x0214> fixed; + fixed.variable = unwrap(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..abbde04 100644 --- a/src/map/clif.hpp +++ b/src/map/clif.hpp @@ -174,6 +174,9 @@ int clif_GM_kick(dumb_ptr sd, dumb_ptr tsd, int type); int clif_foreachclient(std::function)>); +// quest +void clif_sendallquest(dumb_ptr sd); +void clif_sendquest(dumb_ptr 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 resnametable; Map item_db; + Map 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 resnametable; extern Map item_db; + extern Map 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 key, io::Spanned 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/pc.cpp b/src/map/pc.cpp index 1e840ea..0a5df8a 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" @@ -793,7 +794,8 @@ int pc_authok(AccountId id, int login_id2, sd->packet_flood_in = 0; pc_calcstatus(sd, 1); - + // Init Quest Log + clif_sendallquest(sd); return 0; } @@ -3827,14 +3829,37 @@ void pc_setregstr(dumb_ptr sd, SIR reg, RString str) int pc_readglobalreg(dumb_ptr 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> 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 +3872,13 @@ int pc_readglobalreg(dumb_ptr sd, VarName reg) int pc_setglobalreg(dumb_ptr 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("PC_DIE_COUNTER"_s) && sd->die_counter != val) @@ -3856,6 +3886,17 @@ int pc_setglobalreg(dumb_ptr sd, VarName reg, int val) sd->die_counter = val; pc_calcstatus(sd, 0); } + Option> 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 +3904,18 @@ int pc_setglobalreg(dumb_ptr 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 +3925,24 @@ int pc_setglobalreg(dumb_ptr 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, SIR); void pc_setreg(dumb_ptr, SIR, int); ZString pc_readregstr(dumb_ptr sd, SIR reg); void pc_setregstr(dumb_ptr sd, SIR reg, RString str); +void update_quest(dumb_ptr sd, VarName quest_var, int value); +void update_allquest(dumb_ptr sd); int pc_readglobalreg(dumb_ptr, VarName ); int pc_setglobalreg(dumb_ptr, VarName , int); int pc_readaccountreg(dumb_ptr, 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 +// +// 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 . + +#include + +#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 quest, VarName str, Borrowed>> dst) +{ + if (quest->quest_var == str) + *dst = Some(quest); +} + +Option> questdb_searchname(XString str_) +{ + VarName str = stringish(str_); + if (XString(str) != str_) + return None; + Option> quest = None; + for (auto& pair : quest_db) + questdb_searchname_sub(borrow(pair.second), str, borrow(quest)); + return quest; +} + +Borrowed questdb_search(QuestId questid) +{ + Option> id_ = quest_db.search(questid); + OMATCH_BEGIN_SOME (id, id_) + { + return id; + } + OMATCH_END (); + + P id = quest_db.init(questid); + + id->questid = questid; + + return id; +} + +Option> 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 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 +// +// 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 . + +#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> questdb_searchname(VarName) = delete; +Option> questdb_searchname(XString quest_var); +Borrowed questdb_search(QuestId questid); +Option> 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/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 { public: constexpr PartyId() : Wrapped class ItemNameId : public Wrapped { public: constexpr ItemNameId() : Wrapped() {} protected: constexpr explicit ItemNameId(uint16_t a) : Wrapped(a) {} }; class BlockId : public Wrapped { public: constexpr BlockId() : Wrapped() {} protected: constexpr explicit BlockId(uint32_t a) : Wrapped(a) {} }; +class QuestId : public Wrapped { public: constexpr QuestId() : Wrapped() {} protected: constexpr explicit QuestId(uint16_t a) : Wrapped(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') -- cgit v1.2.3-60-g2f50