diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 3ff465d4..99c07fd4 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -289,6 +289,13 @@ struct S_Reconnect_Patch_14 : S_Reconnect { } __packed__; // a key to continue. The maximum length of the message is 0x200 bytes. // This format is shared by multiple commands; for all of them except 06 (S->C), // the guild_card_number field is unused and should be 0. +// During Episode 3 battles, the first byte of an inbound 06 command's message +// is interpreted differently. It should be treated as a bit field, with the low +// 4 bits intended as masks for who can see the message. If the low bit (1) is +// set, for example, then the chat message displays as " (whisper)" on player +// 0's screen regardless of the message contents. The next bit (2) hides the +// message from player 1, etc. The high 4 bits of this byte appear not to be +// used, but are often nonzero and set to the value 4. struct SC_TextHeader_01_06_11_B0_EE { le_uint32_t unused = 0; @@ -4500,6 +4507,10 @@ struct G_WordSelectDuringBattle_GC_Ep3_6xBD { parray entries; le_uint32_t unknown_a4; le_uint32_t unknown_a5; + // This field has the same meaning as the first byte in an 06 command's + // message when sent during an Episode 3 battle. + uint8_t private_flags; + parray unused; } __packed__; // 6xBD: BB bank action (take/deposit meseta/item) (handled by the server) diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 8cff5561..ec4a6324 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1883,8 +1883,7 @@ static void on_game_command(shared_ptr s, shared_ptr c, static void on_chat_generic(shared_ptr s, shared_ptr c, const u16string& text) { // 06 - u16string processed_text = remove_language_marker(text); - if (processed_text.empty()) { + if (text.empty()) { return; } @@ -1893,6 +1892,20 @@ static void on_chat_generic(shared_ptr s, shared_ptr c, return; } + char private_flags = 0; + u16string processed_text; + if ((text[0] != '\t') && + (l->flags & Lobby::Flag::EPISODE_3_ONLY) && + l->ep3_server_base) { + private_flags = text[0]; + processed_text = remove_language_marker(text.substr(1)); + } else { + processed_text = remove_language_marker(text); + } + if (processed_text.empty()) { + return; + } + if (processed_text[0] == L'$') { if (processed_text[1] == L'$') { processed_text = processed_text.substr(1); @@ -1911,7 +1924,8 @@ static void on_chat_generic(shared_ptr s, shared_ptr c, continue; } send_chat_message(l->clients[x], c->license->serial_number, - c->game_data.player()->disp.name.data(), processed_text.c_str()); + c->game_data.player()->disp.name.data(), processed_text.c_str(), + private_flags); } } diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 12a58a74..72f5ac62 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -699,13 +699,17 @@ void send_chat_message(Channel& ch, const u16string& text) { } void send_chat_message(shared_ptr c, uint32_t from_guild_card_number, - const u16string& from_name, const u16string& text) { + const u16string& from_name, const u16string& text, char private_flags) { u16string data; if (c->version() == GameVersion::BB) { - data.append(u"\x09J"); + data.append(u"\tJ"); } data.append(remove_language_marker(from_name)); - data.append(u"\x09\x09J"); + data.append(1, u'\t'); + if (private_flags) { + data.append(1, static_cast(private_flags)); + } + data.append(u"\tJ"); data.append(text); send_header_text(c->channel, 0x06, from_guild_card_number, data, false); } diff --git a/src/SendCommands.hh b/src/SendCommands.hh index 8235fd3e..63fca0d5 100644 --- a/src/SendCommands.hh +++ b/src/SendCommands.hh @@ -175,7 +175,7 @@ void send_text_message(std::shared_ptr l, const std::u16string& text); void send_text_message(std::shared_ptr l, const std::u16string& text); void send_chat_message(Channel& ch, const std::u16string& text); void send_chat_message(std::shared_ptr c, uint32_t from_serial_number, - const std::u16string& from_name, const std::u16string& text); + const std::u16string& from_name, const std::u16string& text, char private_flags); void send_simple_mail(std::shared_ptr c, uint32_t from_serial_number, const std::u16string& from_name, const std::u16string& text);