make quest item exchange implementations more complete
This commit is contained in:
+23
-20
@@ -814,19 +814,20 @@ struct SC_GameGuardCheck_BB_0022 {
|
||||
// used in the case described above; there are no other conditions that cause it to be sent.
|
||||
|
||||
// 23 (S->C): Momoka Item Exchange result (BB)
|
||||
// Sent in response to a 6xD9 command from the client. This command is not valid on BB Trial Edition. header.flag
|
||||
// indicates the result code:
|
||||
// This command is not valid on BB Trial Edition.
|
||||
// Sent in response to a 6xD9 command from the client. header.flag indicates the result code:
|
||||
// 0 = success
|
||||
// 1 = currency item not found
|
||||
// 2 = inventory is full
|
||||
// Anything else = generic failure
|
||||
|
||||
// 24 (S->C): Secret Lottery Ticket exchange result (BB)
|
||||
// This command is not valid on BB Trial Edition.
|
||||
// Sent in response to a 6xDE command from the client. The client sets 8 sequential quest registers, starting with
|
||||
// start_reg_num, to the values specified in reg_values. Then it starts a new quest thread at the specified label.
|
||||
// header.flag indicates whether the client had any Secret Lottery Tickets in their inventory (and hence could
|
||||
// participate): 0 means success, 1 means failure. However, this value is unused by the client.
|
||||
// This command is not valid on BB Trial Edition.
|
||||
// According to logs from Sega's servers, header.flag indicates whether the client had any Secret Lottery Tickets in
|
||||
// their inventory (and hence could participate): 0 means success, 1 means failure. However, this value is unused by
|
||||
// the client.
|
||||
|
||||
struct S_ExchangeSecretLotteryTicketResult_BB_24 {
|
||||
le_uint16_t label = 0;
|
||||
@@ -836,16 +837,17 @@ struct S_ExchangeSecretLotteryTicketResult_BB_24 {
|
||||
} __packed_ws__(S_ExchangeSecretLotteryTicketResult_BB_24, 0x24);
|
||||
|
||||
// 25 (S->C): Gallon's Plan result (BB)
|
||||
// Sent in response to a 6xE1 command from the client. The client sets the quest registers reg_num1 and reg_num2 to
|
||||
// value1 and value2 respectively, then starts a new quest thread at the specified label.
|
||||
// This command is not valid on BB Trial Edition.
|
||||
// Sent in response to a 6xE1 command from the client. The client sets the quest registers result_code_reg and
|
||||
// result_index_reg to result_code_value and result_index_value respectively, then starts a new quest thread at the
|
||||
// specified label.
|
||||
|
||||
struct S_GallonPlanResult_BB_25 {
|
||||
le_uint16_t label = 0;
|
||||
uint8_t reg_num1 = 0;
|
||||
uint8_t reg_num2 = 0;
|
||||
uint8_t value1 = 0;
|
||||
uint8_t value2 = 0;
|
||||
uint8_t result_code_reg = 0;
|
||||
uint8_t result_index_reg = 0;
|
||||
uint8_t result_code_value = 0; // See description of F95F (bb_exchange_pt) in QuestScript.cc for values here
|
||||
uint8_t result_index_value = 0;
|
||||
le_uint16_t unused = 0;
|
||||
} __packed_ws__(S_GallonPlanResult_BB_25, 8);
|
||||
|
||||
@@ -3969,7 +3971,7 @@ struct G_FeedMag_6x28 {
|
||||
le_uint32_t fed_item_id = 0;
|
||||
} __packed_ws__(G_FeedMag_6x28, 0x0C);
|
||||
|
||||
// 6x29: Delete inventory item (via bank deposit / sale / feeding MAG) (protected on GC NTE/V3 but not on V4)
|
||||
// 6x29: Delete inventory item (via bank deposit / sale / feeding MAG) (protected on GC NTE/V3 but not on BB)
|
||||
// This subcommand is also used for reducing the size of stacks - if amount is less than the stack count, the item is
|
||||
// not deleted and its ID remains valid.
|
||||
|
||||
@@ -5957,16 +5959,17 @@ struct G_UpgradeWeaponAttribute_BB_6xDA {
|
||||
le_uint16_t failure_label = 0; // labelH
|
||||
} __packed_ws__(G_UpgradeWeaponAttribute_BB_6xDA, 0x2C);
|
||||
|
||||
// 6xDB: Exchange item in quest (BB)
|
||||
// 6xDB: Extended delete inventory item (BB)
|
||||
|
||||
struct G_ExchangeItemInQuest_BB_6xDB {
|
||||
struct G_ExtendedDeleteInventoryItem_BB_6xDB {
|
||||
G_ClientIDHeader header;
|
||||
// If this is 0, the command is identical to 6x29. If this is 1, a function similar to find_item_by_id is called
|
||||
// instead of find_item_by_id, but I don't yet know what exactly the logic differences are (TODO).
|
||||
le_uint32_t unknown_a1 = 0;
|
||||
// If exclude_wrapped is 0, the command is identical to 6x29; if this is 1, the command won't delete any item which
|
||||
// is wrapped. This seems like an odd feature; shouldn't we expect that the server and client have the same item
|
||||
// state, so the server would already know if the item was wrapped or not before sending this?
|
||||
le_uint32_t exclude_wrapped = 0;
|
||||
le_uint32_t item_id = 0;
|
||||
le_uint32_t amount = 0;
|
||||
} __packed_ws__(G_ExchangeItemInQuest_BB_6xDB, 0x10);
|
||||
} __packed_ws__(G_ExtendedDeleteInventoryItem_BB_6xDB, 0x10);
|
||||
|
||||
// 6xDC: Saint-Milion/Shambertin/Kondrieu boss actions (BB)
|
||||
|
||||
@@ -6027,8 +6030,8 @@ struct G_RequestItemDropFromQuest_BB_6xE0 {
|
||||
|
||||
struct G_ExchangePhotonTickets_BB_6xE1 {
|
||||
G_ClientIDHeader header;
|
||||
uint8_t unknown_a1 = 0; // valueA
|
||||
uint8_t unknown_a2 = 0; // valueB
|
||||
uint8_t result_code_reg = 0; // valueA
|
||||
uint8_t result_index_reg = 0; // valueB
|
||||
uint8_t result_index = 0; // valueC
|
||||
uint8_t unused = 0;
|
||||
le_uint16_t success_label = 0; // valueD
|
||||
|
||||
+7
-3
@@ -2587,12 +2587,16 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = {
|
||||
{0xF95E, "bb_box_create_bp", "BB_box_create_BP", {I32, FLOAT32, FLOAT32}, F_V4 | F_ARGS},
|
||||
|
||||
// Requests an exchange of Photon Tickets for items. Sends 6xE1.
|
||||
// valueA = unknown_a1
|
||||
// valueB = unknown_a2
|
||||
// regA = result code reg (set to result code upon server response); the result codes are:
|
||||
// 0 = success
|
||||
// 1 = player doesn't have enough Photon Tickets
|
||||
// 2 = inventory is full
|
||||
// 3, 4, or 5 = "server send error" (from Gallon's Plan script; newserv never sends these)
|
||||
// regB = result index reg (set to valueC upon server response)
|
||||
// valueC = result index (index into QuestF95FResultItems in config.json)
|
||||
// labelD = label to call on success
|
||||
// labelE = label to call on failure
|
||||
{0xF95F, "bb_exchange_pt", "BB_exchage_PT", {I32, I32, I32, I32, I32}, F_V4 | F_ARGS},
|
||||
{0xF95F, "bb_exchange_pt", "BB_exchage_PT", {W_REG32, W_REG32, I32, I32, I32}, F_V4 | F_ARGS},
|
||||
|
||||
// Requests a prize from the Meseta gambling prize list. Sends 6xE2. The server responds with 6xE3, which sets the
|
||||
// <meseta_slot_prize> replacement token in message strings. The status of this can be checked with
|
||||
|
||||
+31
-31
@@ -5058,16 +5058,9 @@ static asio::awaitable<void> on_secret_lottery_ticket_exchange_bb(shared_ptr<Cli
|
||||
|
||||
uint32_t slt_item_id = p->inventory.items[currency_index].data.id;
|
||||
|
||||
G_ExchangeItemInQuest_BB_6xDB exchange_cmd;
|
||||
exchange_cmd.header.subcommand = 0xDB;
|
||||
exchange_cmd.header.size = 4;
|
||||
exchange_cmd.header.client_id = c->lobby_client_id;
|
||||
exchange_cmd.unknown_a1 = 1;
|
||||
exchange_cmd.item_id = slt_item_id;
|
||||
exchange_cmd.amount = 1;
|
||||
send_command_t(c, 0x60, 0x00, exchange_cmd);
|
||||
|
||||
// Note: It seems Sega used 6xDB here; we use 6x29 instead.
|
||||
p->remove_item(slt_item_id, 1, limits);
|
||||
send_destroy_item_to_lobby(c, slt_item_id, 1);
|
||||
|
||||
item.id = l->generate_item_id(c->lobby_client_id);
|
||||
p->add_item(item, limits);
|
||||
@@ -5163,30 +5156,38 @@ static asio::awaitable<void> on_quest_F95F_result_bb(shared_ptr<Client> c, Subco
|
||||
}
|
||||
|
||||
const auto& limits = *s->item_stack_limits(c->version());
|
||||
size_t index = p->inventory.find_item_by_primary_identifier(0x03100400); // Photon Ticket
|
||||
auto ticket_item = p->remove_item(p->inventory.items[index].data.id, result.first, limits);
|
||||
// TODO: Shouldn't we send a 6x29 here? Check if this causes desync in an actual game
|
||||
|
||||
G_ExchangeItemInQuest_BB_6xDB cmd_6xDB;
|
||||
cmd_6xDB.header = {0xDB, 0x04, c->lobby_client_id};
|
||||
cmd_6xDB.unknown_a1 = 1;
|
||||
cmd_6xDB.item_id = ticket_item.id;
|
||||
cmd_6xDB.amount = result.first;
|
||||
send_command_t(c, 0x60, 0x00, cmd_6xDB);
|
||||
bool failed = false;
|
||||
ItemData ticket_item;
|
||||
try {
|
||||
size_t index = p->inventory.find_item_by_primary_identifier(0x03100400); // Photon Ticket
|
||||
ticket_item = p->remove_item(p->inventory.items[index].data.id, result.first, limits);
|
||||
} catch (const out_of_range&) {
|
||||
failed = true;
|
||||
}
|
||||
if (failed) {
|
||||
send_gallon_plan_result(c, cmd.failure_label, cmd.result_code_reg, 1, cmd.result_index_reg, cmd.result_index);
|
||||
co_return;
|
||||
}
|
||||
|
||||
ItemData new_item = result.second;
|
||||
new_item.enforce_stack_size_limits(limits);
|
||||
new_item.id = l->generate_item_id(c->lobby_client_id);
|
||||
p->add_item(new_item, limits);
|
||||
send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item);
|
||||
try {
|
||||
new_item.enforce_stack_size_limits(limits);
|
||||
new_item.id = l->generate_item_id(c->lobby_client_id);
|
||||
p->add_item(new_item, limits);
|
||||
} catch (const out_of_range&) {
|
||||
failed = true;
|
||||
}
|
||||
if (failed) {
|
||||
p->add_item(ticket_item, limits);
|
||||
send_gallon_plan_result(c, cmd.failure_label, cmd.result_code_reg, 2, cmd.result_index_reg, cmd.result_index);
|
||||
co_return;
|
||||
}
|
||||
|
||||
S_GallonPlanResult_BB_25 out_cmd;
|
||||
out_cmd.label = cmd.success_label;
|
||||
out_cmd.reg_num1 = 0x3C;
|
||||
out_cmd.reg_num2 = 0x08;
|
||||
out_cmd.value1 = 0x00;
|
||||
out_cmd.value2 = cmd.result_index;
|
||||
send_command_t(c, 0x25, 0x00, out_cmd);
|
||||
// Note: It seems Sega used 6xDB here; we use 6x29 instead.
|
||||
send_destroy_item_to_lobby(c, ticket_item.id, result.first);
|
||||
send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item);
|
||||
send_gallon_plan_result(c, cmd.success_label, cmd.result_code_reg, 0, cmd.result_index_reg, cmd.result_index);
|
||||
co_return;
|
||||
}
|
||||
|
||||
@@ -5325,8 +5326,7 @@ static asio::awaitable<void> on_momoka_item_exchange_bb(shared_ptr<Client> c, Su
|
||||
co_return;
|
||||
}
|
||||
|
||||
G_ExchangeItemInQuest_BB_6xDB cmd_6xDB = {{0xDB, 0x04, c->lobby_client_id}, 1, found_item.id, 1};
|
||||
send_command_t(c, 0x60, 0x00, cmd_6xDB);
|
||||
// Note: It seems Sega used 6xDB here; we use 6x29 instead.
|
||||
send_destroy_item_to_lobby(c, found_item.id, 1);
|
||||
send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item);
|
||||
send_command(c, 0x23, 0x00);
|
||||
|
||||
@@ -3307,6 +3307,22 @@ void send_quest_function_call(shared_ptr<Client> c, uint16_t label) {
|
||||
send_quest_function_call(c->channel, label);
|
||||
}
|
||||
|
||||
void send_gallon_plan_result(
|
||||
shared_ptr<Client> c,
|
||||
uint16_t label,
|
||||
uint8_t result_code_reg,
|
||||
uint32_t result_code,
|
||||
uint8_t result_index_reg,
|
||||
uint32_t result_index) {
|
||||
S_GallonPlanResult_BB_25 cmd;
|
||||
cmd.label = label;
|
||||
cmd.result_code_reg = result_code_reg;
|
||||
cmd.result_index_reg = result_index_reg;
|
||||
cmd.result_code_value = result_code;
|
||||
cmd.result_index_value = result_index;
|
||||
send_command_t(c, 0x25, 0x00, cmd);
|
||||
}
|
||||
|
||||
void send_ep3_card_list_update(shared_ptr<Client> c) {
|
||||
if (!c->check_flag(Client::Flag::HAS_EP3_CARD_DEFS)) {
|
||||
auto s = c->require_server_state();
|
||||
|
||||
@@ -380,6 +380,14 @@ void send_rare_enemy_index_list(std::shared_ptr<Client> c, const std::vector<siz
|
||||
void send_quest_function_call(std::shared_ptr<Channel> ch, uint16_t label);
|
||||
void send_quest_function_call(std::shared_ptr<Client> c, uint16_t label);
|
||||
|
||||
void send_gallon_plan_result(
|
||||
std::shared_ptr<Client> c,
|
||||
uint16_t label,
|
||||
uint8_t result_code_reg,
|
||||
uint32_t result_code,
|
||||
uint8_t result_index_reg,
|
||||
uint32_t result_index);
|
||||
|
||||
void send_ep3_card_list_update(std::shared_ptr<Client> c);
|
||||
void send_ep3_media_update(
|
||||
std::shared_ptr<Client> c, uint32_t type, uint32_t which, const std::string& compressed_data);
|
||||
|
||||
Reference in New Issue
Block a user