implement 6xE1
This commit is contained in:
@@ -35,10 +35,9 @@
|
||||
- Find any remaining mismatches in enemy indexes / experience
|
||||
- Fix some edge cases on the BB proxy server (e.g. Change Ship)
|
||||
- Implement less-common subcommands
|
||||
- 6xAC: Sort inventory
|
||||
- 6xC1, 6xC2, 6xCD, 6xCE
|
||||
- 6xCC: Exchange item for team points
|
||||
- 6xD8: Add S-rank weapon special
|
||||
- 6xE1: Gallon's Plan quest
|
||||
- Implement teams
|
||||
- All EA subcommands
|
||||
- 6xC1, 6xC2, 6xCD, 6xCE: Team invites/administration
|
||||
- 6xCC: Exchange item for team points
|
||||
- Implement story progress flags for unlocking quests
|
||||
|
||||
+15
-14
@@ -858,8 +858,9 @@ struct SC_GameGuardCheck_BB_0022 {
|
||||
struct S_ExchangeSecretLotteryTicketResult_BB_24 {
|
||||
// These fields map to unknown_a1 and unknown_a2 in the 6xDE command (but
|
||||
// their order is swapped here).
|
||||
le_uint16_t unknown_a2 = 0;
|
||||
le_uint16_t unknown_a1 = 0;
|
||||
le_uint16_t function_id = 0;
|
||||
uint8_t start_index = 0;
|
||||
uint8_t unused = 0;
|
||||
parray<le_uint32_t, 8> unknown_a3;
|
||||
} __packed__;
|
||||
|
||||
@@ -867,7 +868,7 @@ struct S_ExchangeSecretLotteryTicketResult_BB_24 {
|
||||
// Sent in response to a 6xE1 command from the client.
|
||||
|
||||
struct S_GallonPlanResult_BB_25 {
|
||||
le_uint16_t unknown_a1 = 0;
|
||||
le_uint16_t function_id = 0;
|
||||
uint8_t offset1 = 0;
|
||||
uint8_t offset2 = 0;
|
||||
uint8_t value1 = 0;
|
||||
@@ -5695,7 +5696,7 @@ struct G_ExchangeItemInQuest_BB_6xDB {
|
||||
G_ClientIDHeader header;
|
||||
le_uint32_t unknown_a1 = 0;
|
||||
le_uint32_t item_id = 0;
|
||||
le_uint32_t unknown_a3 = 0;
|
||||
le_uint32_t amount = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6xDC: Saint-Million boss actions (BB)
|
||||
@@ -5719,9 +5720,9 @@ struct G_SetEXPMultiplier_BB_6xDD {
|
||||
|
||||
struct G_ExchangeSecretLotteryTicket_BB_6xDE {
|
||||
G_ClientIDHeader header;
|
||||
uint8_t unknown_a1 = 0;
|
||||
uint8_t unknown_a2 = 0;
|
||||
le_uint16_t unknown_a3 = 0;
|
||||
uint8_t index = 0;
|
||||
uint8_t function_id1 = 0;
|
||||
le_uint16_t function_id2 = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6xDF: Exchange Photon Crystals (BB; handled by server)
|
||||
@@ -5744,17 +5745,17 @@ struct G_RequestItemDropFromQuest_BB_6xE0 {
|
||||
le_float z = 0.0f; // argsA[2]
|
||||
} __packed__;
|
||||
|
||||
// 6xE1: Gallon's Plan quest (BB; handled by server)
|
||||
// 6xE1: Exchange Photon Tickets (BB; handled by server)
|
||||
// The client sends this when it executes an F95F quest opcode.
|
||||
|
||||
struct G_GallonsPlanQuestActions_BB_6xE1 {
|
||||
struct G_ExchangePhotonTickets_BB_6xE1 {
|
||||
G_ClientIDHeader header;
|
||||
uint8_t unknown_a1 = 0;
|
||||
uint8_t unknown_a2 = 0;
|
||||
uint8_t unknown_a3 = 0;
|
||||
uint8_t unknown_a1 = 0; // argsA[0]
|
||||
uint8_t unknown_a2 = 0; // argsA[1]
|
||||
uint8_t result_index = 0; // argsA[2]
|
||||
uint8_t unused = 0;
|
||||
le_uint16_t unknown_a4 = 0;
|
||||
le_uint16_t unknown_a5 = 0;
|
||||
le_uint16_t function_id1 = 0; // argsA[3]
|
||||
le_uint16_t unknown_a5 = 0; // argsA[4]
|
||||
} __packed__;
|
||||
|
||||
// 6xE2: Coren actions (BB)
|
||||
|
||||
@@ -2344,7 +2344,7 @@ static void on_secret_lottery_ticket_exchange_bb(shared_ptr<Client> c, uint8_t,
|
||||
exchange_cmd.header.client_id = c->lobby_client_id;
|
||||
exchange_cmd.unknown_a1 = 1;
|
||||
exchange_cmd.item_id = slt_item_id;
|
||||
exchange_cmd.unknown_a3 = 1;
|
||||
exchange_cmd.amount = 1;
|
||||
send_command_t(c, 0x60, 0x00, exchange_cmd);
|
||||
|
||||
send_destroy_item(c, slt_item_id, 1);
|
||||
@@ -2358,8 +2358,8 @@ static void on_secret_lottery_ticket_exchange_bb(shared_ptr<Client> c, uint8_t,
|
||||
}
|
||||
|
||||
S_ExchangeSecretLotteryTicketResult_BB_24 out_cmd;
|
||||
out_cmd.unknown_a1 = cmd.unknown_a1;
|
||||
out_cmd.unknown_a2 = cmd.unknown_a2;
|
||||
out_cmd.start_index = cmd.index;
|
||||
out_cmd.function_id = cmd.function_id1;
|
||||
if (s->secret_lottery_results.empty()) {
|
||||
out_cmd.unknown_a3.clear(0);
|
||||
} else if (s->secret_lottery_results.size() == 1) {
|
||||
@@ -2415,6 +2415,45 @@ static void on_quest_F95E_result_bb(shared_ptr<Client> c, uint8_t, uint8_t, cons
|
||||
}
|
||||
}
|
||||
|
||||
static void on_quest_F95F_result_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game() && (l->base_version == GameVersion::BB) && l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
const auto& cmd = check_size_t<G_ExchangePhotonTickets_BB_6xE1>(data, size);
|
||||
auto s = c->require_server_state();
|
||||
auto p = c->game_data.character();
|
||||
|
||||
const auto& result = s->quest_F95F_results.at(cmd.result_index);
|
||||
if (result.second.empty()) {
|
||||
throw runtime_error("invalid result index");
|
||||
}
|
||||
|
||||
size_t index = p->inventory.find_item_by_primary_identifier(0x031004); // Photon Ticket
|
||||
auto ticket_item = p->remove_item(p->inventory.items[index].data.id, result.first, false);
|
||||
// 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);
|
||||
|
||||
ItemData new_item = result.second;
|
||||
new_item.id = l->generate_item_id(c->lobby_client_id);
|
||||
p->add_item(new_item);
|
||||
send_create_inventory_item(c, new_item);
|
||||
|
||||
S_GallonPlanResult_BB_25 out_cmd;
|
||||
out_cmd.function_id = cmd.function_id1;
|
||||
out_cmd.offset1 = 0x3C;
|
||||
out_cmd.offset2 = 0x08;
|
||||
out_cmd.value1 = 0x00;
|
||||
out_cmd.value2 = cmd.result_index;
|
||||
send_command_t(c, 0x25, 0x00, out_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_momoka_item_exchange_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game() && (l->base_version == GameVersion::BB) && l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
@@ -2733,7 +2772,7 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
||||
/* 6xDE */ on_secret_lottery_ticket_exchange_bb,
|
||||
/* 6xDF */ on_photon_crystal_exchange_bb,
|
||||
/* 6xE0 */ on_quest_F95E_result_bb,
|
||||
/* 6xE1 */ nullptr,
|
||||
/* 6xE1 */ on_quest_F95F_result_bb,
|
||||
/* 6xE2 */ nullptr,
|
||||
/* 6xE3 */ nullptr,
|
||||
/* 6xE4 */ nullptr,
|
||||
|
||||
+11
-2
@@ -703,7 +703,7 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
|
||||
|
||||
try {
|
||||
this->quest_F95E_results.clear();
|
||||
for (const auto& type_it : json.get_list("QuestF95ERewardItems")) {
|
||||
for (const auto& type_it : json.get_list("QuestF95EResultItems")) {
|
||||
auto& type_res = this->quest_F95E_results.emplace_back();
|
||||
for (const auto& difficulty_it : type_it->as_list()) {
|
||||
auto& difficulty_res = type_res.emplace_back();
|
||||
@@ -715,7 +715,16 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
|
||||
}
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
|
||||
try {
|
||||
this->quest_F95F_results.clear();
|
||||
for (const auto& it : json.get_list("QuestF95FResultItems")) {
|
||||
auto& list = it->as_list();
|
||||
size_t price = list.at(0)->as_int();
|
||||
string data = parse_data_string(list.at(1)->as_string());
|
||||
this->quest_F95F_results.emplace_back(make_pair(price, ItemData::from_data(data)));
|
||||
}
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
try {
|
||||
this->secret_lottery_results.clear();
|
||||
for (const auto& it : json.get_list("SecretLotteryResultItems")) {
|
||||
|
||||
@@ -115,6 +115,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
|
||||
// Indexed as [type][difficulty][random_choice]
|
||||
std::vector<std::vector<std::vector<ItemData>>> quest_F95E_results;
|
||||
std::vector<std::pair<size_t, ItemData>> quest_F95F_results; // [(num_photon_tickets, item)]
|
||||
std::vector<ItemData> secret_lottery_results;
|
||||
uint16_t bb_global_exp_multiplier;
|
||||
|
||||
|
||||
@@ -507,14 +507,14 @@
|
||||
[0x40, "download-ep3", "Download", "$E$C6Quests to download\nto your Memory Card"],
|
||||
],
|
||||
|
||||
// Quest reward item definitions for opcode F95E (used in Black Paper's
|
||||
// Quest result item definitions for opcode F95E (used in Black Paper's
|
||||
// Dangerous Deal 1 and 2). This list is indexed as [reward_type][difficulty].
|
||||
// Reward types 0, 1, 2, and 4 are used by vanilla PSOBB; other reward types
|
||||
// can be specified when using the F95E quest opcode in custom quests. Rewards
|
||||
// are chosen uniformly at random from the list corresponding to the reward
|
||||
// location and difficulty level. All items in these lists must be hex item
|
||||
// codes (1-16 bytes); textual descriptions are not supported here.
|
||||
"QuestF95ERewardItems": [
|
||||
"QuestF95EResultItems": [
|
||||
[
|
||||
["009000", "009001", "009002", "009003", "009004", "009005", "009006", "009007", "009008", "00B400", "01014E", "010307", "010341", "040000", "040000", "040000", "040000", "040000", "040000", "040000", "040000", "040000"],
|
||||
["00B900", "003400", "000901", "009002", "009007", "002C00", "002D00", "010235", "000106", "000105", "040000", "040000", "040000", "040000", "040000", "040000", "040000", "040000", "040000"],
|
||||
@@ -537,7 +537,16 @@
|
||||
["00BA00", "00B400", "000D03", "00B600", "00B300", "000708", "004301", "00C900", "010136", "01028A", "010299", "010285", "010348", "010351", "01035B", "010352"],
|
||||
],
|
||||
],
|
||||
// Reward item definitions for Secret Lottery Ticket exchange (quest opcode
|
||||
// Results for quest opcode F95F (used in Gallon's Plan). This list is indexed
|
||||
// by type (which is the third argument to the opcode). The entries here are
|
||||
// [num_photon_tickets, item_hex].
|
||||
"QuestF95FResultItems": [
|
||||
[0, ""], // Unused
|
||||
[10, "00D500"],
|
||||
[15, "000A07"],
|
||||
[20, "010157"],
|
||||
],
|
||||
// Result item definitions for Secret Lottery Ticket exchange (quest opcode
|
||||
// F95C, used in the Good Luck quest).
|
||||
"SecretLotteryResultItems": [
|
||||
"000106", "000107", "000206", "000407", "000606", "000807", "000D01",
|
||||
|
||||
+7
-1
@@ -146,7 +146,7 @@
|
||||
[0x40, "download-ep3", "Download", "$E$C6Quests to download\nto your Memory Card"],
|
||||
],
|
||||
|
||||
"QuestF95ERewardItems": [
|
||||
"QuestF95EResultItems": [
|
||||
[
|
||||
["009000", "009001", "009002", "009003", "009004", "009005", "009006", "009007", "009008", "00B400", "01014E", "010307", "010341", "040000", "040000", "040000", "040000", "040000", "040000", "040000", "040000", "040000"],
|
||||
["00B900", "003400", "000901", "009002", "009007", "002C00", "002D00", "010235", "000106", "000105", "040000", "040000", "040000", "040000", "040000", "040000", "040000", "040000", "040000"],
|
||||
@@ -169,6 +169,12 @@
|
||||
["00BA00", "00B400", "000D03", "00B600", "00B300", "000708", "004301", "00C900", "010136", "01028A", "010299", "010285", "010348", "010351", "01035B", "010352"],
|
||||
],
|
||||
],
|
||||
"QuestF95FResultItems": [
|
||||
[0, ""], // Unused
|
||||
[10, "00D500"],
|
||||
[15, "000A07"],
|
||||
[20, "010157"],
|
||||
],
|
||||
"SecretLotteryResultItems": [
|
||||
"000106", "000107", "000206", "000407", "000606", "000807", "000D01",
|
||||
"001300", "002000", "002700", "002C00", "003400", "003900", "003C00",
|
||||
|
||||
Reference in New Issue
Block a user