fix challenge mode times window
This commit is contained in:
+24
-7
@@ -2022,8 +2022,19 @@ template <TextEncoding Encoding, size_t ShortDescLength>
|
||||
struct S_QuestMenuEntryT {
|
||||
// Note: The game treats menu_id as two 8-bit fields followed by a 16-bit
|
||||
// field. In most situations, this is opaque to the server, so we treat it as
|
||||
// a single 32-bit field, but in the case of the quest menu, the second byte
|
||||
// is used to determine the icon that appears to the left of the quest name.
|
||||
// a single 32-bit field; however, in the case of the quest menu, the first
|
||||
// and second bytes have meaning on the client.
|
||||
//
|
||||
// The first byte is used as the quest episode number, which is only relevant
|
||||
// for showing the Challenge Mode times window when a quest is selected.
|
||||
// This byte must be set correctly on the quest category entry, not the quest
|
||||
// itself, so the Episode 1 Challenge quests category should have a value of
|
||||
// 1 in this byte, and the Episode 2 Challenge quests category should have a
|
||||
// value of 2. (This is not the only condition required for the Challenge
|
||||
// Mode times window to work; see the description of command A3 also.)
|
||||
//
|
||||
// The second byte of the menu ID is used to determine which icon appears to
|
||||
// the left of the quest name.
|
||||
// Specifically:
|
||||
// 0 = online quest icon (green diamond)
|
||||
// 1 = download quest icon (green square with outlined diamond)
|
||||
@@ -2054,7 +2065,13 @@ struct S_QuestMenuEntry_BB_A2_A4 {
|
||||
} __packed_ws__(S_QuestMenuEntry_BB_A2_A4, 0x13C);
|
||||
|
||||
// A3 (S->C): Quest information
|
||||
// Same format as 1A/D5 command (plain text)
|
||||
// Same format as 1A/D5 command (plain text). The header.flag field is used to
|
||||
// inform the client of the Challenge stage number, so it can show the correct
|
||||
// timing window when the stage is selected. The Episode 1 stage numbers should
|
||||
// be specified as 51-59 (decimal) in header.flag, and the Episode 2 stage
|
||||
// numbers should be specified as 61-65 (decimal). If the header.flag value is
|
||||
// outside this range, it is ignored, and the Challenge Mode times window does
|
||||
// not update.
|
||||
|
||||
// A4 (S->C): Download quest menu
|
||||
// Internal name: RcvQuestList
|
||||
@@ -5220,12 +5237,12 @@ struct G_PlayerKilledByMonster_6x89 {
|
||||
le_uint16_t unused = 0;
|
||||
} __packed_ws__(G_PlayerKilledByMonster_6x89, 8);
|
||||
|
||||
// 6x8A: Unknown (not valid on Episode 3)
|
||||
// 6x8A: Show Challenge time records window (not valid on Episode 3)
|
||||
|
||||
struct G_Unknown_6x8A {
|
||||
struct G_ShowChallengeTimeRecordsWindow_6x8A {
|
||||
G_ClientIDHeader header;
|
||||
le_uint32_t unknown_a1 = 0; // Must be < 0x11
|
||||
} __packed_ws__(G_Unknown_6x8A, 8);
|
||||
le_uint32_t which = 0; // Must be < 0x11
|
||||
} __packed_ws__(G_ShowChallengeTimeRecordsWindow_6x8A, 8);
|
||||
|
||||
// 6x8B: Unknown (not valid on Episode 3)
|
||||
// This command has a handler, but it does nothing.
|
||||
|
||||
+2
-1
@@ -20,7 +20,8 @@ constexpr uint32_t LOBBY = 0x33000033;
|
||||
constexpr uint32_t GAME = 0x44000044;
|
||||
constexpr uint32_t QUEST_EP1 = 0x55010155;
|
||||
constexpr uint32_t QUEST_EP2 = 0x55020255;
|
||||
constexpr uint32_t QUEST_CATEGORIES = 0x66010166;
|
||||
constexpr uint32_t QUEST_CATEGORIES_EP1 = 0x01666601;
|
||||
constexpr uint32_t QUEST_CATEGORIES_EP2 = 0x02666602;
|
||||
constexpr uint32_t PROXY_DESTINATIONS = 0x77000077;
|
||||
constexpr uint32_t PROGRAMS = 0x88000088;
|
||||
constexpr uint32_t PATCHES = 0x99000099;
|
||||
|
||||
@@ -204,6 +204,7 @@ VersionedQuest::VersionedQuest(
|
||||
std::shared_ptr<const std::string> pvr_contents,
|
||||
std::shared_ptr<const BattleRules> battle_rules,
|
||||
ssize_t challenge_template_index,
|
||||
uint8_t description_flag,
|
||||
std::shared_ptr<const IntegralExpression> available_expression,
|
||||
std::shared_ptr<const IntegralExpression> enabled_expression,
|
||||
bool allow_start_from_chat_command,
|
||||
@@ -223,6 +224,7 @@ VersionedQuest::VersionedQuest(
|
||||
pvr_contents(pvr_contents),
|
||||
battle_rules(battle_rules),
|
||||
challenge_template_index(challenge_template_index),
|
||||
description_flag(description_flag),
|
||||
available_expression(available_expression),
|
||||
enabled_expression(enabled_expression) {
|
||||
|
||||
@@ -392,6 +394,7 @@ Quest::Quest(shared_ptr<const VersionedQuest> initial_version)
|
||||
name(initial_version->name),
|
||||
battle_rules(initial_version->battle_rules),
|
||||
challenge_template_index(initial_version->challenge_template_index),
|
||||
description_flag(initial_version->description_flag),
|
||||
available_expression(initial_version->available_expression),
|
||||
enabled_expression(initial_version->enabled_expression) {
|
||||
this->versions.emplace(this->versions_key(initial_version->version, initial_version->language), initial_version);
|
||||
@@ -429,6 +432,9 @@ void Quest::add_version(shared_ptr<const VersionedQuest> vq) {
|
||||
if (this->challenge_template_index != vq->challenge_template_index) {
|
||||
throw runtime_error("quest version has different challenge template index");
|
||||
}
|
||||
if (this->description_flag != vq->description_flag) {
|
||||
throw runtime_error("quest version has different description flag");
|
||||
}
|
||||
if (!this->available_expression != !vq->available_expression) {
|
||||
throw runtime_error("quest version has available expression but root quest does not, or vice versa");
|
||||
}
|
||||
@@ -680,6 +686,7 @@ QuestIndex::QuestIndex(
|
||||
const FileData* json_filedata = nullptr;
|
||||
shared_ptr<BattleRules> battle_rules;
|
||||
ssize_t challenge_template_index = -1;
|
||||
uint8_t description_flag = 0;
|
||||
shared_ptr<const IntegralExpression> available_expression;
|
||||
shared_ptr<const IntegralExpression> enabled_expression;
|
||||
bool allow_start_from_chat_command = false;
|
||||
@@ -707,6 +714,10 @@ QuestIndex::QuestIndex(
|
||||
challenge_template_index = metadata_json.at("ChallengeTemplateIndex").as_int();
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
try {
|
||||
description_flag = metadata_json.at("DescriptionFlag").as_int();
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
try {
|
||||
available_expression = make_shared<IntegralExpression>(metadata_json.get_string("AvailableIf"));
|
||||
} catch (const out_of_range&) {
|
||||
@@ -739,6 +750,7 @@ QuestIndex::QuestIndex(
|
||||
pvr_filedata ? pvr_filedata->data : nullptr,
|
||||
battle_rules,
|
||||
challenge_template_index,
|
||||
description_flag,
|
||||
available_expression,
|
||||
enabled_expression,
|
||||
allow_start_from_chat_command,
|
||||
|
||||
+8
-2
@@ -37,7 +37,7 @@ enum class QuestMenuType {
|
||||
struct QuestCategoryIndex {
|
||||
struct Category {
|
||||
uint32_t category_id;
|
||||
uint8_t enabled_flags;
|
||||
uint16_t enabled_flags;
|
||||
std::string directory_name;
|
||||
std::string name;
|
||||
std::string description;
|
||||
@@ -48,7 +48,10 @@ struct QuestCategoryIndex {
|
||||
return this->enabled_flags & (1 << static_cast<uint8_t>(menu_type));
|
||||
}
|
||||
[[nodiscard]] inline bool enable_episode_filter() const {
|
||||
return this->enabled_flags & 0x80;
|
||||
return this->enabled_flags & 0x080;
|
||||
}
|
||||
[[nodiscard]] inline bool use_ep2_icon() const {
|
||||
return this->enabled_flags & 0x100;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -78,6 +81,7 @@ struct VersionedQuest {
|
||||
std::shared_ptr<const std::string> pvr_contents;
|
||||
std::shared_ptr<const BattleRules> battle_rules;
|
||||
ssize_t challenge_template_index;
|
||||
uint8_t description_flag;
|
||||
std::shared_ptr<const IntegralExpression> available_expression;
|
||||
std::shared_ptr<const IntegralExpression> enabled_expression;
|
||||
|
||||
@@ -91,6 +95,7 @@ struct VersionedQuest {
|
||||
std::shared_ptr<const std::string> pvr_contents,
|
||||
std::shared_ptr<const BattleRules> battle_rules = nullptr,
|
||||
ssize_t challenge_template_index = -1,
|
||||
uint8_t description_flag = 0,
|
||||
std::shared_ptr<const IntegralExpression> available_expression = nullptr,
|
||||
std::shared_ptr<const IntegralExpression> enabled_expression = nullptr,
|
||||
bool allow_start_from_chat_command = false,
|
||||
@@ -131,6 +136,7 @@ public:
|
||||
std::string name;
|
||||
std::shared_ptr<const BattleRules> battle_rules;
|
||||
ssize_t challenge_template_index;
|
||||
uint8_t description_flag;
|
||||
std::shared_ptr<const IntegralExpression> available_expression;
|
||||
std::shared_ptr<const IntegralExpression> enabled_expression;
|
||||
std::map<uint32_t, std::shared_ptr<const VersionedQuest>> versions;
|
||||
|
||||
@@ -1744,7 +1744,8 @@ static void on_09(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
auto s = c->require_server_state();
|
||||
|
||||
switch (cmd.menu_id) {
|
||||
case MenuID::QUEST_CATEGORIES:
|
||||
case MenuID::QUEST_CATEGORIES_EP1:
|
||||
case MenuID::QUEST_CATEGORIES_EP2:
|
||||
// Don't send anything here. The quest filter menu already has short
|
||||
// descriptions included with the entries, which the client shows in the
|
||||
// usual location on the screen.
|
||||
@@ -1754,17 +1755,17 @@ static void on_09(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
bool is_download_quest = !c->lobby.lock();
|
||||
auto quest_index = s->quest_index(c->version());
|
||||
if (!quest_index) {
|
||||
send_quest_info(c, "$C7Quests are not available.", is_download_quest);
|
||||
send_quest_info(c, "$C7Quests are not available.", 0x00, is_download_quest);
|
||||
} else {
|
||||
auto q = quest_index->get(cmd.item_id);
|
||||
if (!q) {
|
||||
send_quest_info(c, "$C4Quest does not\nexist.", is_download_quest);
|
||||
send_quest_info(c, "$C4Quest does not\nexist.", 0x00, is_download_quest);
|
||||
} else {
|
||||
auto vq = q->version(c->version(), c->language());
|
||||
if (!vq) {
|
||||
send_quest_info(c, "$C4Quest does not\nexist for this game\nversion.", is_download_quest);
|
||||
send_quest_info(c, "$C4Quest does not\nexist for this game\nversion.", 0x00, is_download_quest);
|
||||
} else {
|
||||
send_quest_info(c, vq->long_description, is_download_quest);
|
||||
send_quest_info(c, vq->long_description, vq->description_flag, is_download_quest);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2470,7 +2471,8 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
break;
|
||||
}
|
||||
|
||||
case MenuID::QUEST_CATEGORIES: {
|
||||
case MenuID::QUEST_CATEGORIES_EP1:
|
||||
case MenuID::QUEST_CATEGORIES_EP2: {
|
||||
auto s = c->require_server_state();
|
||||
auto quest_index = s->quest_index(c->version());
|
||||
if (!quest_index) {
|
||||
|
||||
+10
-9
@@ -739,6 +739,7 @@ static void send_text(
|
||||
Channel& ch,
|
||||
StringWriter& w,
|
||||
uint16_t command,
|
||||
uint32_t flag,
|
||||
const string& text,
|
||||
ColorMode color_mode) {
|
||||
bool is_w = uses_utf16(ch.version);
|
||||
@@ -772,18 +773,18 @@ static void send_text(
|
||||
while (w.str().size() & 3) {
|
||||
w.put_u8(0);
|
||||
}
|
||||
ch.send(command, 0x00, w.str());
|
||||
ch.send(command, flag, w.str());
|
||||
}
|
||||
|
||||
static void send_text(Channel& ch, uint16_t command, const string& text, ColorMode color_mode) {
|
||||
static void send_text(Channel& ch, uint16_t command, uint32_t flag, const string& text, ColorMode color_mode) {
|
||||
StringWriter w;
|
||||
send_text(ch, w, command, text, color_mode);
|
||||
send_text(ch, w, command, flag, text, color_mode);
|
||||
}
|
||||
|
||||
static void send_header_text(Channel& ch, uint16_t command, uint32_t guild_card_number, const string& text, ColorMode color_mode) {
|
||||
StringWriter w;
|
||||
w.put(SC_TextHeader_01_06_11_B0_EE({0, guild_card_number}));
|
||||
send_text(ch, w, command, text, color_mode);
|
||||
send_text(ch, w, command, 0x00, text, color_mode);
|
||||
}
|
||||
|
||||
void send_message_box(shared_ptr<Client> c, const string& text) {
|
||||
@@ -812,7 +813,7 @@ void send_message_box(shared_ptr<Client> c, const string& text) {
|
||||
default:
|
||||
throw logic_error("invalid game version");
|
||||
}
|
||||
send_text(c->channel, command, text, ColorMode::ADD);
|
||||
send_text(c->channel, command, 0x00, text, ColorMode::ADD);
|
||||
}
|
||||
|
||||
void send_ep3_timed_message_box(Channel& ch, uint32_t frames, const string& message) {
|
||||
@@ -834,11 +835,11 @@ void send_ep3_timed_message_box(Channel& ch, uint32_t frames, const string& mess
|
||||
}
|
||||
|
||||
void send_lobby_name(shared_ptr<Client> c, const string& text) {
|
||||
send_text(c->channel, 0x8A, text, ColorMode::NONE);
|
||||
send_text(c->channel, 0x8A, 0x00, text, ColorMode::NONE);
|
||||
}
|
||||
|
||||
void send_quest_info(shared_ptr<Client> c, const string& text, bool is_download_quest) {
|
||||
send_text(c->channel, is_download_quest ? 0xA5 : 0xA3, text, ColorMode::ADD);
|
||||
void send_quest_info(shared_ptr<Client> c, const string& text, uint8_t description_flag, bool is_download_quest) {
|
||||
send_text(c->channel, is_download_quest ? 0xA5 : 0xA3, description_flag, text, ColorMode::ADD);
|
||||
}
|
||||
|
||||
void send_lobby_message_box(shared_ptr<Client> c, const string& text, bool left_side_on_bb) {
|
||||
@@ -1590,7 +1591,7 @@ void send_quest_categories_menu_t(
|
||||
vector<EntryT> entries;
|
||||
for (const auto& cat : quest_index->categories(menu_type, episode, c->version(), include_condition)) {
|
||||
auto& e = entries.emplace_back();
|
||||
e.menu_id = MenuID::QUEST_CATEGORIES;
|
||||
e.menu_id = cat->use_ep2_icon() ? MenuID::QUEST_CATEGORIES_EP2 : MenuID::QUEST_CATEGORIES_EP1;
|
||||
e.item_id = cat->category_id;
|
||||
e.name.encode(cat->name, c->language());
|
||||
e.short_description.encode(add_color(cat->description), c->language());
|
||||
|
||||
+1
-2
@@ -200,8 +200,7 @@ void send_complete_player_bb(std::shared_ptr<Client> c);
|
||||
void send_message_box(std::shared_ptr<Client> c, const std::string& text);
|
||||
void send_ep3_timed_message_box(Channel& ch, uint32_t frames, const std::string& text);
|
||||
void send_lobby_name(std::shared_ptr<Client> c, const std::string& text);
|
||||
void send_quest_info(std::shared_ptr<Client> c, const std::string& text,
|
||||
bool is_download_quest);
|
||||
void send_quest_info(std::shared_ptr<Client> c, const std::string& text, uint8_t flag, bool is_download_quest);
|
||||
void send_lobby_message_box(std::shared_ptr<Client> c, const std::string& text, bool left_side_on_bb = false);
|
||||
void send_ship_info(std::shared_ptr<Client> c, const std::string& text);
|
||||
void send_ship_info(Channel& ch, const std::string& text);
|
||||
|
||||
Reference in New Issue
Block a user