diff --git a/README.md b/README.md index 1ac215ca..d3869007 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ Some commands only work on the game server and not on the proxy server. The chat * Personal state commands * `$arrow `: Changes your lobby arrow color. * `$secid `: Sets your override section ID. After running this command, any games you create will use your override section ID for rare drops instead of your character's actual section ID. To revert to your actual section id, run `$secid` with no name after it. + * `$rand `: Sets your override random seed (specified as a 32-bit hex value). This will make any games you create use the given seed for rare enemies. This also makes item drops deterministic in Blue Burst games hosted by newserv. On the proxy server, this command can cause desyncs with other players in the same game, since they will not see the overridden random seed. To remove the override, run `$rand` with no arguments. * Blue Burst player commands (game server only) * `$bbchar <1-4>`: Use this command when playing on a non-BB version of PSO. If the username and password are correct, this command converts your current character to BB format and saves it on the server in the given slot. diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 9177c129..d2f866fc 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -333,6 +333,30 @@ static void proxy_command_secid(shared_ptr, } } +static void server_command_rand(shared_ptr, shared_ptr l, + shared_ptr c, const std::u16string& args) { + check_is_game(l, false); + + if (!args[0]) { + c->override_random_seed = -1; + send_text_message(c, u"$C6Override seed\nremoved"); + } else { + c->override_random_seed = stoul(encode_sjis(args), 0, 16); + send_text_message(c, u"$C6Override seed\nset"); + } +} + +static void proxy_command_rand(shared_ptr, + ProxyServer::LinkedSession& session, const std::u16string& args) { + if (!args[0]) { + session.override_random_seed = -1; + send_text_message(session.client_channel, u"$C6Override seed\nremoved"); + } else { + session.override_random_seed = stoul(encode_sjis(args), 0, 16); + send_text_message(session.client_channel, u"$C6Override seed\nset"); + } +} + static void server_command_password(shared_ptr, shared_ptr l, shared_ptr c, const std::u16string& args) { check_is_game(l, true); @@ -828,6 +852,7 @@ static const unordered_map chat_commands({ // TODO: implement this on proxy server {u"$next" , {server_command_next , nullptr , u"Usage:\nnext"}}, {u"$password" , {server_command_password , nullptr , u"Usage:\nlock [password]\nomit password to\nunlock game"}}, + {u"$rand" , {server_command_rand , proxy_command_rand , u"Usage:\nrand [hex seed]\nomit seed to revert\nto default"}}, {u"$secid" , {server_command_secid , proxy_command_secid , u"Usage:\nsecid [section ID]\nomit section ID to\nrevert to normal"}}, {u"$silence" , {server_command_silence , nullptr , u"Usage:\nsilence "}}, // TODO: implement this on proxy server diff --git a/src/Client.cc b/src/Client.cc index 2294cebd..69aac99f 100644 --- a/src/Client.cc +++ b/src/Client.cc @@ -47,6 +47,7 @@ Client::Client( prefer_high_lobby_client_id(false), next_exp_value(0), override_section_id(-1), + override_random_seed(-1), infinite_hp(false), infinite_tp(false), switch_assist(false), diff --git a/src/Client.hh b/src/Client.hh index 1763ae4c..8de04dbd 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -92,6 +92,7 @@ struct Client { // Miscellaneous (used by chat commands) uint32_t next_exp_value; // next EXP value to give int16_t override_section_id; // valid if >= 0 + int64_t override_random_seed; // valid if >= 0 bool infinite_hp; // cheats enabled bool infinite_tp; // cheats enabled bool switch_assist; // cheats enabled diff --git a/src/Items.cc b/src/Items.cc index 9fc00987..600ca402 100644 --- a/src/Items.cc +++ b/src/Items.cc @@ -8,6 +8,13 @@ using namespace std; +uint32_t random_int(shared_ptr rand, uint32_t min, uint32_t max) { + uint32_t range = max - min + 1; + return min + ((*rand)() % range); +} + + + //////////////////////////////////////////////////////////////////////////////// /* these items all need some kind of special handling that hasn't been implemented yet. @@ -226,14 +233,13 @@ void player_use_item(shared_ptr c, size_t item_index) { //////////////////////////////////////////////////////////////////////////////// -// reads the non-rare item preferences from the config file. -CommonItemCreator::CommonItemCreator( - const vector& enemy_item_categories, - const vector& box_item_categories, - const vector>& unit_types) : - enemy_item_categories(enemy_item_categories), - box_item_categories(box_item_categories), - unit_types(unit_types) { +CommonItemData::CommonItemData( + vector&& enemy_item_categories, + vector&& box_item_categories, + vector>&& unit_types) : + enemy_item_categories(move(enemy_item_categories)), + box_item_categories(move(box_item_categories)), + unit_types(move(unit_types)) { // sanity check the values if (this->enemy_item_categories.size() != 8) { @@ -267,16 +273,21 @@ CommonItemCreator::CommonItemCreator( } } -int32_t CommonItemCreator::decide_item_type(bool is_box) const { - uint32_t determinant = random_object(); +CommonItemCreator::CommonItemCreator( + std::shared_ptr data, + std::shared_ptr random) + : data(data), random(random) { } - const auto* v = is_box ? &this->box_item_categories : &this->enemy_item_categories; - for (size_t x = 0; x < v->size(); x++) { - uint32_t probability = v->at(x); - if (probability > determinant) { +int32_t CommonItemCreator::decide_item_type(bool is_box) const { + uint32_t det = (*this->random)(); + + const auto& v = is_box ? this->data->box_item_categories : this->data->enemy_item_categories; + for (size_t x = 0; x < v.size(); x++) { + uint32_t probability = v.at(x); + if (probability > det) { return x; } - determinant -= probability; + det -= probability; } return -1; } @@ -325,25 +336,25 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode, case 0x00: // material item.data1[0] = 0x03; item.data1[1] = 0x0B; - item.data1[2] = random_int(0, 6); + item.data1[2] = random_int(this->random, 0, 6); break; case 0x01: // equipment - switch (random_int(0, 3)) { + switch (random_int(this->random, 0, 3)) { case 0x00: // weapon - item.data1[1] = random_int(1, 12); // random normal class - item.data1[2] = difficulty + random_int(0, 2); // special type + item.data1[1] = random_int(this->random, 1, 12); // random normal class + item.data1[2] = difficulty + random_int(this->random, 0, 2); // special type if ((item.data1[1] > 0x09) && (item.data1[2] > 0x04)) { item.data1[2] = 0x04; // no special classes above 4 } item.data1[4] = 0x80; // untekked if (item.data1[2] < 0x04) { - item.data1[4] |= random_int(0, 40); // give a special + item.data1[4] |= random_int(this->random, 0, 40); // give a special } for (size_t x = 0, y = 0; (x < 5) && (y < 3); x++) { // percentages - if (random_int(0, 10) == 1) { // 1/11 chance of getting each type of percentage + if (random_int(this->random, 0, 10) == 1) { // 1/11 chance of getting each type of percentage item.data1[6 + (y * 2)] = x + 1; - item.data1[7 + (y * 2)] = random_int(0, 10) * 5; + item.data1[7 + (y * 2)] = random_int(this->random, 0, 10) * 5; y++; } } @@ -352,33 +363,33 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode, case 0x01: // armor item.data1[0] = 0x01; item.data1[1] = 0x01; - item.data1[2] = (6 * difficulty) + random_int(0, ((area / 2) + 2) - 1); // standard type based on difficulty and area + item.data1[2] = (6 * difficulty) + random_int(this->random, 0, ((area / 2) + 2) - 1); // standard type based on difficulty and area if (item.data1[2] > 0x17) { item.data1[2] = 0x17; // no standard types above 0x17 } - if (random_int(0, 10) == 0) { // +/- - item.data1[4] = random_int(0, 5); - item.data1[6] = random_int(0, 2); + if (random_int(this->random, 0, 10) == 0) { // +/- + item.data1[4] = random_int(this->random, 0, 5); + item.data1[6] = random_int(this->random, 0, 2); } - item.data1[5] = random_int(0, 4); // slots + item.data1[5] = random_int(this->random, 0, 4); // slots break; case 0x02: // shield item.data1[0] = 0x01; item.data1[1] = 0x02; - item.data1[2] = (5 * difficulty) + random_int(0, ((area / 2) + 2) - 1); // standard type based on difficulty and area + item.data1[2] = (5 * difficulty) + random_int(this->random, 0, ((area / 2) + 2) - 1); // standard type based on difficulty and area if (item.data1[2] > 0x14) { item.data1[2] = 0x14; // no standard types above 0x14 } - if (random_int(0, 10) == 0) { // +/- - item.data1[4] = random_int(0, 5); - item.data1[6] = random_int(0, 5); + if (random_int(this->random, 0, 10) == 0) { // +/- + item.data1[4] = random_int(this->random, 0, 5); + item.data1[6] = random_int(this->random, 0, 5); } break; case 0x03: { // unit - const auto& type_table = this->unit_types.at(difficulty); - uint8_t type = type_table[random_int(0, type_table.size() - 1)]; + const auto& type_table = this->data->unit_types.at(difficulty); + uint8_t type = type_table[random_int(this->random, 0, type_table.size() - 1)]; if (type == 0xFF) { throw out_of_range("no item dropped"); // 0xFF -> no item drops } @@ -393,11 +404,11 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode, case 0x02: // technique item.data1[0] = 0x03; item.data1[1] = 0x02; - item.data1[4] = random_int(0, 18); // tech type + item.data1[4] = random_int(this->random, 0, 18); // tech type if ((item.data1[4] != 14) && (item.data1[4] != 17)) { // if not ryuker or reverser, give it a level if (item.data1[4] == 16) { // if not anti, give it a level between 1 and 30 if (area > 3) { - item.data1[2] = difficulty + random_int(0, ((area - 1) / 2) - 1); + item.data1[2] = difficulty + random_int(this->random, 0, ((area - 1) / 2) - 1); } else { item.data1[2] = difficulty; } @@ -405,7 +416,7 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode, item.data1[2] = 6; } } else { - item.data1[2] = (5 * difficulty) + random_int(0, ((area * 3) / 2) - 1); // else between 1 and 7 + item.data1[2] = (5 * difficulty) + random_int(this->random, 0, ((area * 3) / 2) - 1); // else between 1 and 7 } } break; @@ -419,24 +430,24 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode, case 0x04: // grinder item.data1[0] = 0x03; item.data1[1] = 0x0A; - item.data1[2] = random_int(0, 2); // mono, di, tri + item.data1[2] = random_int(this->random, 0, 2); // mono, di, tri break; case 0x05: // consumable item.data1[0] = 0x03; item.data1[5] = 0x01; - switch (random_int(0, 2)) { + switch (random_int(this->random, 0, 2)) { case 0: // antidote / antiparalysis item.data1[1] = 6; - item.data1[2] = random_int(0, 1); + item.data1[2] = random_int(this->random, 0, 1); break; case 1: // telepipe / trap vision - item.data1[1] = 7 + random_int(0, 1); + item.data1[1] = 7 + random_int(this->random, 0, 1); break; case 2: // sol / moon / star atomizer - item.data1[1] = 3 + random_int(0, 2); + item.data1[1] = 3 + random_int(this->random, 0, 2); break; } break; @@ -444,19 +455,19 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode, case 0x06: // consumable item.data1[0] = 0x03; item.data1[5] = 0x01; - item.data1[1] = random_int(0, 1); // mate or fluid + item.data1[1] = random_int(this->random, 0, 1); // mate or fluid if (difficulty == 0) { - item.data1[2] = random_int(0, 1); // only mono and di on normal + item.data1[2] = random_int(this->random, 0, 1); // only mono and di on normal } else if (difficulty == 3) { - item.data1[2] = random_int(1, 2); // only di and tri on ultimate + item.data1[2] = random_int(this->random, 1, 2); // only di and tri on ultimate } else { - item.data1[2] = random_int(0, 2); // else, any of the three + item.data1[2] = random_int(this->random, 0, 2); // else, any of the three } break; case 0x07: // meseta item.data1[0] = 0x04; - item.data2d = (90 * difficulty) + (random_int(1, 20) * (area * 2)); // meseta amount + item.data2d = (90 * difficulty) + (random_int(this->random, 1, 20) * (area * 2)); // meseta amount break; default: diff --git a/src/Items.hh b/src/Items.hh index a1c005fd..a1dc749d 100644 --- a/src/Items.hh +++ b/src/Items.hh @@ -3,20 +3,32 @@ #include #include +#include -#include "Lobby.hh" #include "Client.hh" + + void player_use_item(std::shared_ptr c, size_t item_index); -struct CommonItemCreator { +struct CommonItemData { std::vector enemy_item_categories; std::vector box_item_categories; std::vector> unit_types; - CommonItemCreator(const std::vector& enemy_item_categories, - const std::vector& box_item_categories, - const std::vector>& unit_types); + CommonItemData( + std::vector&& enemy_item_categories, + std::vector&& box_item_categories, + std::vector>&& unit_types); +}; + +struct CommonItemCreator { + std::shared_ptr data; + std::shared_ptr random; + + CommonItemCreator( + std::shared_ptr data, + std::shared_ptr random); int32_t decide_item_type(bool is_box) const; ItemData create_drop_item(bool is_box, uint8_t episode, uint8_t difficulty, diff --git a/src/Lobby.cc b/src/Lobby.cc index ec03dc96..c24721b6 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -23,7 +23,8 @@ Lobby::Lobby(uint32_t id) episode(1), difficulty(0), mode(0), - rare_seed(random_object()), + random_seed(random_object()), + random(new mt19937(this->random_seed)), event(0), block(0), type(0), diff --git a/src/Lobby.hh b/src/Lobby.hh index 7dc391c6..cc694e97 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -15,6 +16,7 @@ #include "RareItemSet.hh" #include "Text.hh" #include "Quest.hh" +#include "Items.hh" struct Lobby { enum Flag { @@ -63,9 +65,10 @@ struct Lobby { uint8_t mode; std::u16string password; std::u16string name; - uint32_t rare_seed; - - //EP3_GAME_CONFIG* ep3; // only present if this is an Episode 3 game + // This seed is also sent to the client for rare enemy generation + uint32_t random_seed; + std::shared_ptr random; + std::shared_ptr common_item_creator; // lobby stuff uint8_t event; diff --git a/src/Main.cc b/src/Main.cc index 70e5d124..d0473609 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -74,14 +74,16 @@ void populate_state_from_config(shared_ptr s, s->set_port_configuration(parse_port_configuration(d.at("PortConfiguration"))); - auto enemy_categories = parse_int_vector(d.at("CommonItemDropRates-Enemy")); - auto box_categories = parse_int_vector(d.at("CommonItemDropRates-Box")); - vector> unit_types; - for (const auto& item : d.at("CommonUnitTypes")->as_list()) { - unit_types.emplace_back(parse_int_vector(item)); + { + auto enemy_categories = parse_int_vector(d.at("CommonItemDropRates-Enemy")); + auto box_categories = parse_int_vector(d.at("CommonItemDropRates-Box")); + vector> unit_types; + for (const auto& item : d.at("CommonUnitTypes")->as_list()) { + unit_types.emplace_back(parse_int_vector(item)); + } + s->common_item_data.reset(new CommonItemData( + move(enemy_categories), move(box_categories), move(unit_types))); } - s->common_item_creator.reset(new CommonItemCreator(enemy_categories, - box_categories, unit_types)); auto local_address_str = d.at("LocalAddress")->as_string(); try { diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index b0142848..06215a03 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -841,6 +841,10 @@ static HandlerResult process_server_64(shared_ptr, cmd.event = session.override_lobby_event; modified = true; } + if (session.override_random_seed >= 0) { + cmd.rare_seed = session.override_random_seed; + modified = true; + } return modified ? HandlerResult::MODIFIED : HandlerResult::FORWARD; } diff --git a/src/ProxyServer.hh b/src/ProxyServer.hh index 7ad2211e..a5b8f535 100644 --- a/src/ProxyServer.hh +++ b/src/ProxyServer.hh @@ -69,6 +69,7 @@ public: int16_t override_section_id; int16_t override_lobby_event; int16_t override_lobby_number; + int64_t override_random_seed; struct LobbyPlayer { uint32_t guild_card_number; diff --git a/src/RareItemSet.cc b/src/RareItemSet.cc index 975c2124..5ba1d16b 100644 --- a/src/RareItemSet.cc +++ b/src/RareItemSet.cc @@ -14,11 +14,11 @@ RareItemSet::RareItemSet(const char* filename, uint8_t episode, preadx(fd, this, sizeof(*this), offset); } -bool sample_rare_item(uint8_t pc) { +bool sample_rare_item(mt19937& random, uint8_t pc) { int8_t shift = ((pc >> 3) & 0x1F) - 4; if (shift < 0) { shift = 0; } uint32_t rate = ((2 << shift) * ((pc & 7) + 7)); - return (random_object() < rate); + return (random() < rate); } diff --git a/src/RareItemSet.hh b/src/RareItemSet.hh index a779cb6c..28a81a32 100644 --- a/src/RareItemSet.hh +++ b/src/RareItemSet.hh @@ -2,6 +2,8 @@ #include +#include + struct RareItemDrop { @@ -24,4 +26,4 @@ struct RareItemSet { uint8_t secid); } __attribute__((packed)); -bool sample_rare_item(uint8_t pc); +bool sample_rare_item(std::mt19937& rand, uint8_t pc); diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index c99b3e4d..58f01f38 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1865,6 +1865,12 @@ shared_ptr create_game_generic(shared_ptr s, if (solo) { game->mode = 3; } + if (c->override_random_seed >= 0) { + game->random_seed = c->override_random_seed; + game->random->seed(game->random_seed); + } + game->common_item_creator.reset(new CommonItemCreator( + s->common_item_data, game->random)); game->event = Lobby::game_event_for_lobby_event(current_lobby->event); game->block = 0xFF; game->max_clients = 4; @@ -1927,7 +1933,7 @@ shared_ptr create_game_generic(shared_ptr s, if (variation_maxes) { for (size_t x = 0; x < 0x20; x++) { - game->variations.data()[x] = random_int(0, variation_maxes[x] - 1); + game->variations.data()[x] = (*game->random)() % variation_maxes[x]; } } else { for (size_t x = 0; x < 0x20; x++) { diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 78d2d5b3..7afc3482 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -544,13 +544,16 @@ static void process_subcommand_use_item(shared_ptr, forward_subcommand(l, c, command, flag, data); } -static void process_subcommand_open_shop_bb_or_unknown_ep3(shared_ptr s, +static void process_subcommand_open_shop_bb_or_unknown_ep3(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, const string& data) { if (l->flags & Lobby::Flag::EPISODE_3_ONLY) { check_size_sc(data, 0x08, 0xFFFF); forward_subcommand(l, c, command, flag, data); + } else if (!l->common_item_creator.get()) { + throw runtime_error("received shop subcommand without item creator present"); + } else { const auto* p = check_size_sc(data, 0x08); uint32_t shop_type = p[1].dword; @@ -561,11 +564,11 @@ static void process_subcommand_open_shop_bb_or_unknown_ep3(shared_ptrgame_data.shop_contents.size() < num_items) { ItemData item_data; if (shop_type == 0) { // tool shop - item_data = s->common_item_creator->create_shop_item(l->difficulty, 3); + item_data = l->common_item_creator->create_shop_item(l->difficulty, 3); } else if (shop_type == 1) { // weapon shop - item_data = s->common_item_creator->create_shop_item(l->difficulty, 0); + item_data = l->common_item_creator->create_shop_item(l->difficulty, 0); } else if (shop_type == 2) { // guards shop - item_data = s->common_item_creator->create_shop_item(l->difficulty, 1); + item_data = l->common_item_creator->create_shop_item(l->difficulty, 1); } else { // unknown shop... just leave it blank I guess break; } @@ -668,7 +671,7 @@ static void process_subcommand_sort_inventory_bb(shared_ptr, //////////////////////////////////////////////////////////////////////////////// // EXP/Drop Item commands -static void process_subcommand_enemy_drop_item_request(shared_ptr s, +static void process_subcommand_enemy_drop_item_request(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, const string& data) { if (l->version == GameVersion::BB) { @@ -677,10 +680,12 @@ static void process_subcommand_enemy_drop_item_request(shared_ptr s if (!l->is_game()) { return; } - if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { throw logic_error("item tracking not enabled in BB game"); } + if (!l->common_item_creator.get()) { + throw runtime_error("received box drop subcommand without item creator present"); + } PlayerInventoryItem item; @@ -692,7 +697,8 @@ static void process_subcommand_enemy_drop_item_request(shared_ptr s } else { if (l->rare_item_set) { if (cmd->enemy_id <= 0x65) { - is_rare = sample_rare_item(l->rare_item_set->rares[cmd->enemy_id].probability); + is_rare = sample_rare_item(* + l->random, l->rare_item_set->rares[cmd->enemy_id].probability); } } @@ -707,7 +713,7 @@ static void process_subcommand_enemy_drop_item_request(shared_ptr s } } else { try { - item.data = s->common_item_creator->create_drop_item(false, l->episode, + item.data = l->common_item_creator->create_drop_item(false, l->episode, l->difficulty, cmd->area, l->section_id); } catch (const out_of_range&) { // create_common_item throws this when it doesn't want to make an item @@ -726,7 +732,7 @@ static void process_subcommand_enemy_drop_item_request(shared_ptr s } } -static void process_subcommand_box_drop_item_request(shared_ptr s, +static void process_subcommand_box_drop_item_request(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, const string& data) { if (l->version == GameVersion::BB) { @@ -735,10 +741,12 @@ static void process_subcommand_box_drop_item_request(shared_ptr s, if (!l->is_game()) { return; } - if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { throw logic_error("item tracking not enabled in BB game"); } + if (!l->common_item_creator.get()) { + throw runtime_error("received box drop subcommand without item creator present"); + } PlayerInventoryItem item; @@ -753,7 +761,8 @@ static void process_subcommand_box_drop_item_request(shared_ptr s, if (l->rare_item_set->box_areas[index] != cmd->area) { continue; } - if (sample_rare_item(l->rare_item_set->box_rares[index].probability)) { + if (sample_rare_item( + *l->random, l->rare_item_set->box_rares[index].probability)) { is_rare = true; break; } @@ -771,7 +780,7 @@ static void process_subcommand_box_drop_item_request(shared_ptr s, } } else { try { - item.data = s->common_item_creator->create_drop_item(true, l->episode, + item.data = l->common_item_creator->create_drop_item(true, l->episode, l->difficulty, cmd->area, l->section_id); } catch (const out_of_range&) { // create_common_item throws this when it doesn't want to make an item diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 1e6fe9a6..0298e954 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -919,7 +919,7 @@ void send_join_game_t(shared_ptr c, shared_ptr l) { cmd.event = l->event; cmd.section_id = l->section_id; cmd.challenge_mode = (l->mode == 2) ? 1 : 0; - cmd.rare_seed = l->rare_seed; + cmd.rare_seed = l->random_seed; cmd.episode = l->episode; cmd.unused2 = 0x01; cmd.solo_mode = (l->mode == 3); diff --git a/src/ServerState.hh b/src/ServerState.hh index c6e216f1..dce20a77 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -56,7 +56,7 @@ struct ServerState { std::shared_ptr quest_index; std::shared_ptr level_table; std::shared_ptr battle_params; - std::shared_ptr common_item_creator; + std::shared_ptr common_item_data; std::shared_ptr license_manager;