implement 6xE0 command
This commit is contained in:
@@ -5709,17 +5709,17 @@ struct G_BlackPaperDealPhotonCrystalExchange_BB_6xDF {
|
||||
G_ClientIDHeader header;
|
||||
} __packed__;
|
||||
|
||||
// 6xE0: Black Paper's Deal rewards (BB; handled by server)
|
||||
// 6xE0: Request item drop from quest (BB; handled by server)
|
||||
// The client sends this when it executes an F95E quest opcode.
|
||||
|
||||
struct G_BlackPaperDealRewards_BB_6xE0 {
|
||||
struct G_RequestItemDropFromQuest_BB_6xE0 {
|
||||
G_ClientIDHeader header;
|
||||
uint8_t unknown_a1 = 0;
|
||||
uint8_t unknown_a2 = 0; // argsA[0]
|
||||
uint8_t floor = 0;
|
||||
uint8_t type = 0; // argsA[0]
|
||||
uint8_t unknown_a3 = 0;
|
||||
uint8_t unknown_a4 = 0;
|
||||
le_float unknown_a5 = 0.0f; // argsA[1]
|
||||
le_float unknown_a6 = 0.0f; // argsA[2]
|
||||
uint8_t unused = 0;
|
||||
le_float x = 0.0f; // argsA[1]
|
||||
le_float z = 0.0f; // argsA[2]
|
||||
} __packed__;
|
||||
|
||||
// 6xE1: Gallon's Plan quest (BB; handled by server)
|
||||
|
||||
@@ -585,6 +585,21 @@ bool ItemData::compare_for_sort(const ItemData& a, const ItemData& b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemData ItemData::from_data(const string& data) {
|
||||
if (data.size() > 0x10) {
|
||||
throw runtime_error("data is too long");
|
||||
}
|
||||
|
||||
ItemData ret;
|
||||
for (size_t z = 0; z < min<size_t>(data.size(), 12); z++) {
|
||||
ret.data1[z] = data[z];
|
||||
}
|
||||
for (size_t z = 12; z < min<size_t>(data.size(), 16); z++) {
|
||||
ret.data2[z - 12] = data[z];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
string ItemData::hex() const {
|
||||
return string_printf("%02hhX%02hhX%02hhX%02hhX %02hhX%02hhX%02hhX%02hhX %02hhX%02hhX%02hhX%02hhX (%08" PRIX32 ") %02hhX%02hhX%02hhX%02hhX",
|
||||
this->data1[0], this->data1[1], this->data1[2], this->data1[3],
|
||||
|
||||
@@ -111,6 +111,7 @@ struct ItemData { // 0x14 bytes
|
||||
|
||||
void clear();
|
||||
|
||||
static ItemData from_data(const std::string& data);
|
||||
std::string hex() const;
|
||||
uint32_t primary_identifier() const;
|
||||
|
||||
|
||||
@@ -2286,6 +2286,36 @@ static void on_photon_crystal_exchange_bb(shared_ptr<Client> c, uint8_t, uint8_t
|
||||
}
|
||||
}
|
||||
|
||||
static void on_quest_F95E_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_RequestItemDropFromQuest_BB_6xE0>(data, size);
|
||||
auto s = c->require_server_state();
|
||||
|
||||
size_t count = (cmd.type > 0x03) ? 1 : (l->difficulty + 1);
|
||||
for (size_t z = 0; z < count; z++) {
|
||||
const auto& results = s->quest_F95E_results.at(cmd.type).at(l->difficulty);
|
||||
if (results.empty()) {
|
||||
throw runtime_error("invalid result type");
|
||||
}
|
||||
ItemData item = (results.size() == 1) ? results[0] : results[random_object<uint32_t>() % results.size()];
|
||||
if (item.data1[0] == 0x04) { // Meseta
|
||||
// TODO: What is the right amount of Meseta to use here? Presumably it
|
||||
// should be random within a certain range, but it's not obvious what
|
||||
// that range should be.
|
||||
item.data2d = 100;
|
||||
} else if (item.data1[0] == 0x00) {
|
||||
item.data1[4] |= 0x80; // Unidentified
|
||||
}
|
||||
|
||||
item.id = l->generate_item_id(0xFF);
|
||||
l->add_item(item, cmd.floor, cmd.x, cmd.z);
|
||||
|
||||
send_drop_stacked_item(l, item, cmd.floor, cmd.x, cmd.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
@@ -2603,7 +2633,7 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
||||
/* 6xDD */ nullptr,
|
||||
/* 6xDE */ nullptr,
|
||||
/* 6xDF */ on_photon_crystal_exchange_bb,
|
||||
/* 6xE0 */ nullptr,
|
||||
/* 6xE0 */ on_quest_F95E_result_bb,
|
||||
/* 6xE1 */ nullptr,
|
||||
/* 6xE2 */ nullptr,
|
||||
/* 6xE3 */ nullptr,
|
||||
|
||||
@@ -700,6 +700,21 @@ 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")) {
|
||||
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();
|
||||
for (const auto& item_it : difficulty_it->as_list()) {
|
||||
string data = parse_data_string(item_it->as_string());
|
||||
difficulty_res.emplace_back(ItemData::from_data(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
|
||||
set_log_levels_from_json(json.get("LogLevels", JSON::dict()));
|
||||
|
||||
if (!is_reload) {
|
||||
|
||||
@@ -113,6 +113,9 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
std::shared_ptr<const ItemNameIndex> item_name_index;
|
||||
std::shared_ptr<const WordSelectTable> word_select_table;
|
||||
|
||||
// Indexed as [type][difficulty][random_choice]
|
||||
std::vector<std::vector<std::vector<ItemData>>> quest_F95E_results;
|
||||
|
||||
std::shared_ptr<Episode3::TournamentIndex> ep3_tournament_index;
|
||||
|
||||
uint16_t ep3_card_auction_points;
|
||||
|
||||
Reference in New Issue
Block a user