add $rand command

This commit is contained in:
Martin Michelsen
2022-07-11 11:04:26 -07:00
parent abe64af17b
commit 0a115427a1
17 changed files with 159 additions and 80 deletions
+1
View File
@@ -89,6 +89,7 @@ Some commands only work on the game server and not on the proxy server. The chat
* Personal state commands * Personal state commands
* `$arrow <color-id>`: Changes your lobby arrow color. * `$arrow <color-id>`: Changes your lobby arrow color.
* `$secid <section-id>`: 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. * `$secid <section-id>`: 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 <seed>`: 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) * Blue Burst player commands (game server only)
* `$bbchar <username> <password> <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. * `$bbchar <username> <password> <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.
+25
View File
@@ -333,6 +333,30 @@ static void proxy_command_secid(shared_ptr<ServerState>,
} }
} }
static void server_command_rand(shared_ptr<ServerState>, shared_ptr<Lobby> l,
shared_ptr<Client> 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<ServerState>,
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<ServerState>, shared_ptr<Lobby> l, static void server_command_password(shared_ptr<ServerState>, shared_ptr<Lobby> l,
shared_ptr<Client> c, const std::u16string& args) { shared_ptr<Client> c, const std::u16string& args) {
check_is_game(l, true); check_is_game(l, true);
@@ -828,6 +852,7 @@ static const unordered_map<u16string, ChatCommandDefinition> chat_commands({
// TODO: implement this on proxy server // TODO: implement this on proxy server
{u"$next" , {server_command_next , nullptr , u"Usage:\nnext"}}, {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"$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"$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 <name-or-number>"}}, {u"$silence" , {server_command_silence , nullptr , u"Usage:\nsilence <name-or-number>"}},
// TODO: implement this on proxy server // TODO: implement this on proxy server
+1
View File
@@ -47,6 +47,7 @@ Client::Client(
prefer_high_lobby_client_id(false), prefer_high_lobby_client_id(false),
next_exp_value(0), next_exp_value(0),
override_section_id(-1), override_section_id(-1),
override_random_seed(-1),
infinite_hp(false), infinite_hp(false),
infinite_tp(false), infinite_tp(false),
switch_assist(false), switch_assist(false),
+1
View File
@@ -92,6 +92,7 @@ struct Client {
// Miscellaneous (used by chat commands) // Miscellaneous (used by chat commands)
uint32_t next_exp_value; // next EXP value to give uint32_t next_exp_value; // next EXP value to give
int16_t override_section_id; // valid if >= 0 int16_t override_section_id; // valid if >= 0
int64_t override_random_seed; // valid if >= 0
bool infinite_hp; // cheats enabled bool infinite_hp; // cheats enabled
bool infinite_tp; // cheats enabled bool infinite_tp; // cheats enabled
bool switch_assist; // cheats enabled bool switch_assist; // cheats enabled
+57 -46
View File
@@ -8,6 +8,13 @@ using namespace std;
uint32_t random_int(shared_ptr<mt19937> 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. /* 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<Client> c, size_t item_index) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// reads the non-rare item preferences from the config file. CommonItemData::CommonItemData(
CommonItemCreator::CommonItemCreator( vector<uint32_t>&& enemy_item_categories,
const vector<uint32_t>& enemy_item_categories, vector<uint32_t>&& box_item_categories,
const vector<uint32_t>& box_item_categories, vector<vector<uint8_t>>&& unit_types) :
const vector<vector<uint8_t>>& unit_types) : enemy_item_categories(move(enemy_item_categories)),
enemy_item_categories(enemy_item_categories), box_item_categories(move(box_item_categories)),
box_item_categories(box_item_categories), unit_types(move(unit_types)) {
unit_types(unit_types) {
// sanity check the values // sanity check the values
if (this->enemy_item_categories.size() != 8) { if (this->enemy_item_categories.size() != 8) {
@@ -267,16 +273,21 @@ CommonItemCreator::CommonItemCreator(
} }
} }
int32_t CommonItemCreator::decide_item_type(bool is_box) const { CommonItemCreator::CommonItemCreator(
uint32_t determinant = random_object<uint32_t>(); std::shared_ptr<const CommonItemData> data,
std::shared_ptr<std::mt19937> random)
: data(data), random(random) { }
const auto* v = is_box ? &this->box_item_categories : &this->enemy_item_categories; int32_t CommonItemCreator::decide_item_type(bool is_box) const {
for (size_t x = 0; x < v->size(); x++) { uint32_t det = (*this->random)();
uint32_t probability = v->at(x);
if (probability > determinant) { 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; return x;
} }
determinant -= probability; det -= probability;
} }
return -1; return -1;
} }
@@ -325,25 +336,25 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode,
case 0x00: // material case 0x00: // material
item.data1[0] = 0x03; item.data1[0] = 0x03;
item.data1[1] = 0x0B; item.data1[1] = 0x0B;
item.data1[2] = random_int(0, 6); item.data1[2] = random_int(this->random, 0, 6);
break; break;
case 0x01: // equipment case 0x01: // equipment
switch (random_int(0, 3)) { switch (random_int(this->random, 0, 3)) {
case 0x00: // weapon case 0x00: // weapon
item.data1[1] = random_int(1, 12); // random normal class item.data1[1] = random_int(this->random, 1, 12); // random normal class
item.data1[2] = difficulty + random_int(0, 2); // special type item.data1[2] = difficulty + random_int(this->random, 0, 2); // special type
if ((item.data1[1] > 0x09) && (item.data1[2] > 0x04)) { if ((item.data1[1] > 0x09) && (item.data1[2] > 0x04)) {
item.data1[2] = 0x04; // no special classes above 4 item.data1[2] = 0x04; // no special classes above 4
} }
item.data1[4] = 0x80; // untekked item.data1[4] = 0x80; // untekked
if (item.data1[2] < 0x04) { 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 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[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++; y++;
} }
} }
@@ -352,33 +363,33 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode,
case 0x01: // armor case 0x01: // armor
item.data1[0] = 0x01; item.data1[0] = 0x01;
item.data1[1] = 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) { if (item.data1[2] > 0x17) {
item.data1[2] = 0x17; // no standard types above 0x17 item.data1[2] = 0x17; // no standard types above 0x17
} }
if (random_int(0, 10) == 0) { // +/- if (random_int(this->random, 0, 10) == 0) { // +/-
item.data1[4] = random_int(0, 5); item.data1[4] = random_int(this->random, 0, 5);
item.data1[6] = random_int(0, 2); 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; break;
case 0x02: // shield case 0x02: // shield
item.data1[0] = 0x01; item.data1[0] = 0x01;
item.data1[1] = 0x02; 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) { if (item.data1[2] > 0x14) {
item.data1[2] = 0x14; // no standard types above 0x14 item.data1[2] = 0x14; // no standard types above 0x14
} }
if (random_int(0, 10) == 0) { // +/- if (random_int(this->random, 0, 10) == 0) { // +/-
item.data1[4] = random_int(0, 5); item.data1[4] = random_int(this->random, 0, 5);
item.data1[6] = random_int(0, 5); item.data1[6] = random_int(this->random, 0, 5);
} }
break; break;
case 0x03: { // unit case 0x03: { // unit
const auto& type_table = this->unit_types.at(difficulty); const auto& type_table = this->data->unit_types.at(difficulty);
uint8_t type = type_table[random_int(0, type_table.size() - 1)]; uint8_t type = type_table[random_int(this->random, 0, type_table.size() - 1)];
if (type == 0xFF) { if (type == 0xFF) {
throw out_of_range("no item dropped"); // 0xFF -> no item drops 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 case 0x02: // technique
item.data1[0] = 0x03; item.data1[0] = 0x03;
item.data1[1] = 0x02; 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] != 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 (item.data1[4] == 16) { // if not anti, give it a level between 1 and 30
if (area > 3) { 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 { } else {
item.data1[2] = difficulty; item.data1[2] = difficulty;
} }
@@ -405,7 +416,7 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode,
item.data1[2] = 6; item.data1[2] = 6;
} }
} else { } 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; break;
@@ -419,24 +430,24 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode,
case 0x04: // grinder case 0x04: // grinder
item.data1[0] = 0x03; item.data1[0] = 0x03;
item.data1[1] = 0x0A; 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; break;
case 0x05: // consumable case 0x05: // consumable
item.data1[0] = 0x03; item.data1[0] = 0x03;
item.data1[5] = 0x01; item.data1[5] = 0x01;
switch (random_int(0, 2)) { switch (random_int(this->random, 0, 2)) {
case 0: // antidote / antiparalysis case 0: // antidote / antiparalysis
item.data1[1] = 6; item.data1[1] = 6;
item.data1[2] = random_int(0, 1); item.data1[2] = random_int(this->random, 0, 1);
break; break;
case 1: // telepipe / trap vision case 1: // telepipe / trap vision
item.data1[1] = 7 + random_int(0, 1); item.data1[1] = 7 + random_int(this->random, 0, 1);
break; break;
case 2: // sol / moon / star atomizer 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;
} }
break; break;
@@ -444,19 +455,19 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode,
case 0x06: // consumable case 0x06: // consumable
item.data1[0] = 0x03; item.data1[0] = 0x03;
item.data1[5] = 0x01; 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) { 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) { } 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 { } 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; break;
case 0x07: // meseta case 0x07: // meseta
item.data1[0] = 0x04; 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; break;
default: default:
+17 -5
View File
@@ -3,20 +3,32 @@
#include <stdint.h> #include <stdint.h>
#include <memory> #include <memory>
#include <random>
#include "Lobby.hh"
#include "Client.hh" #include "Client.hh"
void player_use_item(std::shared_ptr<Client> c, size_t item_index); void player_use_item(std::shared_ptr<Client> c, size_t item_index);
struct CommonItemCreator { struct CommonItemData {
std::vector<uint32_t> enemy_item_categories; std::vector<uint32_t> enemy_item_categories;
std::vector<uint32_t> box_item_categories; std::vector<uint32_t> box_item_categories;
std::vector<std::vector<uint8_t>> unit_types; std::vector<std::vector<uint8_t>> unit_types;
CommonItemCreator(const std::vector<uint32_t>& enemy_item_categories, CommonItemData(
const std::vector<uint32_t>& box_item_categories, std::vector<uint32_t>&& enemy_item_categories,
const std::vector<std::vector<uint8_t>>& unit_types); std::vector<uint32_t>&& box_item_categories,
std::vector<std::vector<uint8_t>>&& unit_types);
};
struct CommonItemCreator {
std::shared_ptr<const CommonItemData> data;
std::shared_ptr<std::mt19937> random;
CommonItemCreator(
std::shared_ptr<const CommonItemData> data,
std::shared_ptr<std::mt19937> random);
int32_t decide_item_type(bool is_box) const; int32_t decide_item_type(bool is_box) const;
ItemData create_drop_item(bool is_box, uint8_t episode, uint8_t difficulty, ItemData create_drop_item(bool is_box, uint8_t episode, uint8_t difficulty,
+2 -1
View File
@@ -23,7 +23,8 @@ Lobby::Lobby(uint32_t id)
episode(1), episode(1),
difficulty(0), difficulty(0),
mode(0), mode(0),
rare_seed(random_object<uint32_t>()), random_seed(random_object<uint32_t>()),
random(new mt19937(this->random_seed)),
event(0), event(0),
block(0), block(0),
type(0), type(0),
+6 -3
View File
@@ -2,6 +2,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <random>
#include <array> #include <array>
#include <vector> #include <vector>
#include <string> #include <string>
@@ -15,6 +16,7 @@
#include "RareItemSet.hh" #include "RareItemSet.hh"
#include "Text.hh" #include "Text.hh"
#include "Quest.hh" #include "Quest.hh"
#include "Items.hh"
struct Lobby { struct Lobby {
enum Flag { enum Flag {
@@ -63,9 +65,10 @@ struct Lobby {
uint8_t mode; uint8_t mode;
std::u16string password; std::u16string password;
std::u16string name; std::u16string name;
uint32_t rare_seed; // This seed is also sent to the client for rare enemy generation
uint32_t random_seed;
//EP3_GAME_CONFIG* ep3; // only present if this is an Episode 3 game std::shared_ptr<std::mt19937> random;
std::shared_ptr<const CommonItemCreator> common_item_creator;
// lobby stuff // lobby stuff
uint8_t event; uint8_t event;
+4 -2
View File
@@ -74,14 +74,16 @@ void populate_state_from_config(shared_ptr<ServerState> s,
s->set_port_configuration(parse_port_configuration(d.at("PortConfiguration"))); s->set_port_configuration(parse_port_configuration(d.at("PortConfiguration")));
{
auto enemy_categories = parse_int_vector<uint32_t>(d.at("CommonItemDropRates-Enemy")); auto enemy_categories = parse_int_vector<uint32_t>(d.at("CommonItemDropRates-Enemy"));
auto box_categories = parse_int_vector<uint32_t>(d.at("CommonItemDropRates-Box")); auto box_categories = parse_int_vector<uint32_t>(d.at("CommonItemDropRates-Box"));
vector<vector<uint8_t>> unit_types; vector<vector<uint8_t>> unit_types;
for (const auto& item : d.at("CommonUnitTypes")->as_list()) { for (const auto& item : d.at("CommonUnitTypes")->as_list()) {
unit_types.emplace_back(parse_int_vector<uint8_t>(item)); unit_types.emplace_back(parse_int_vector<uint8_t>(item));
} }
s->common_item_creator.reset(new CommonItemCreator(enemy_categories, s->common_item_data.reset(new CommonItemData(
box_categories, unit_types)); move(enemy_categories), move(box_categories), move(unit_types)));
}
auto local_address_str = d.at("LocalAddress")->as_string(); auto local_address_str = d.at("LocalAddress")->as_string();
try { try {
+4
View File
@@ -841,6 +841,10 @@ static HandlerResult process_server_64(shared_ptr<ServerState>,
cmd.event = session.override_lobby_event; cmd.event = session.override_lobby_event;
modified = true; modified = true;
} }
if (session.override_random_seed >= 0) {
cmd.rare_seed = session.override_random_seed;
modified = true;
}
return modified ? HandlerResult::MODIFIED : HandlerResult::FORWARD; return modified ? HandlerResult::MODIFIED : HandlerResult::FORWARD;
} }
+1
View File
@@ -69,6 +69,7 @@ public:
int16_t override_section_id; int16_t override_section_id;
int16_t override_lobby_event; int16_t override_lobby_event;
int16_t override_lobby_number; int16_t override_lobby_number;
int64_t override_random_seed;
struct LobbyPlayer { struct LobbyPlayer {
uint32_t guild_card_number; uint32_t guild_card_number;
+2 -2
View File
@@ -14,11 +14,11 @@ RareItemSet::RareItemSet(const char* filename, uint8_t episode,
preadx(fd, this, sizeof(*this), offset); 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; int8_t shift = ((pc >> 3) & 0x1F) - 4;
if (shift < 0) { if (shift < 0) {
shift = 0; shift = 0;
} }
uint32_t rate = ((2 << shift) * ((pc & 7) + 7)); uint32_t rate = ((2 << shift) * ((pc & 7) + 7));
return (random_object<uint32_t>() < rate); return (random() < rate);
} }
+3 -1
View File
@@ -2,6 +2,8 @@
#include <stdint.h> #include <stdint.h>
#include <random>
struct RareItemDrop { struct RareItemDrop {
@@ -24,4 +26,4 @@ struct RareItemSet {
uint8_t secid); uint8_t secid);
} __attribute__((packed)); } __attribute__((packed));
bool sample_rare_item(uint8_t pc); bool sample_rare_item(std::mt19937& rand, uint8_t pc);
+7 -1
View File
@@ -1865,6 +1865,12 @@ shared_ptr<Lobby> create_game_generic(shared_ptr<ServerState> s,
if (solo) { if (solo) {
game->mode = 3; 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->event = Lobby::game_event_for_lobby_event(current_lobby->event);
game->block = 0xFF; game->block = 0xFF;
game->max_clients = 4; game->max_clients = 4;
@@ -1927,7 +1933,7 @@ shared_ptr<Lobby> create_game_generic(shared_ptr<ServerState> s,
if (variation_maxes) { if (variation_maxes) {
for (size_t x = 0; x < 0x20; x++) { 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 { } else {
for (size_t x = 0; x < 0x20; x++) { for (size_t x = 0; x < 0x20; x++) {
+21 -12
View File
@@ -544,13 +544,16 @@ static void process_subcommand_use_item(shared_ptr<ServerState>,
forward_subcommand(l, c, command, flag, data); forward_subcommand(l, c, command, flag, data);
} }
static void process_subcommand_open_shop_bb_or_unknown_ep3(shared_ptr<ServerState> s, static void process_subcommand_open_shop_bb_or_unknown_ep3(shared_ptr<ServerState>,
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag, shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
const string& data) { const string& data) {
if (l->flags & Lobby::Flag::EPISODE_3_ONLY) { if (l->flags & Lobby::Flag::EPISODE_3_ONLY) {
check_size_sc(data, 0x08, 0xFFFF); check_size_sc(data, 0x08, 0xFFFF);
forward_subcommand(l, c, command, flag, data); 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 { } else {
const auto* p = check_size_sc(data, 0x08); const auto* p = check_size_sc(data, 0x08);
uint32_t shop_type = p[1].dword; uint32_t shop_type = p[1].dword;
@@ -561,11 +564,11 @@ static void process_subcommand_open_shop_bb_or_unknown_ep3(shared_ptr<ServerStat
while (c->game_data.shop_contents.size() < num_items) { while (c->game_data.shop_contents.size() < num_items) {
ItemData item_data; ItemData item_data;
if (shop_type == 0) { // tool shop 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 } 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 } 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 } else { // unknown shop... just leave it blank I guess
break; break;
} }
@@ -668,7 +671,7 @@ static void process_subcommand_sort_inventory_bb(shared_ptr<ServerState>,
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// EXP/Drop Item commands // EXP/Drop Item commands
static void process_subcommand_enemy_drop_item_request(shared_ptr<ServerState> s, static void process_subcommand_enemy_drop_item_request(shared_ptr<ServerState>,
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag, shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
const string& data) { const string& data) {
if (l->version == GameVersion::BB) { if (l->version == GameVersion::BB) {
@@ -677,10 +680,12 @@ static void process_subcommand_enemy_drop_item_request(shared_ptr<ServerState> s
if (!l->is_game()) { if (!l->is_game()) {
return; return;
} }
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
throw logic_error("item tracking not enabled in BB game"); 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; PlayerInventoryItem item;
@@ -692,7 +697,8 @@ static void process_subcommand_enemy_drop_item_request(shared_ptr<ServerState> s
} else { } else {
if (l->rare_item_set) { if (l->rare_item_set) {
if (cmd->enemy_id <= 0x65) { 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<ServerState> s
} }
} else { } else {
try { 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); l->difficulty, cmd->area, l->section_id);
} catch (const out_of_range&) { } catch (const out_of_range&) {
// create_common_item throws this when it doesn't want to make an item // 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<ServerState> s
} }
} }
static void process_subcommand_box_drop_item_request(shared_ptr<ServerState> s, static void process_subcommand_box_drop_item_request(shared_ptr<ServerState>,
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag, shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
const string& data) { const string& data) {
if (l->version == GameVersion::BB) { if (l->version == GameVersion::BB) {
@@ -735,10 +741,12 @@ static void process_subcommand_box_drop_item_request(shared_ptr<ServerState> s,
if (!l->is_game()) { if (!l->is_game()) {
return; return;
} }
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
throw logic_error("item tracking not enabled in BB game"); 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; PlayerInventoryItem item;
@@ -753,7 +761,8 @@ static void process_subcommand_box_drop_item_request(shared_ptr<ServerState> s,
if (l->rare_item_set->box_areas[index] != cmd->area) { if (l->rare_item_set->box_areas[index] != cmd->area) {
continue; 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; is_rare = true;
break; break;
} }
@@ -771,7 +780,7 @@ static void process_subcommand_box_drop_item_request(shared_ptr<ServerState> s,
} }
} else { } else {
try { 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); l->difficulty, cmd->area, l->section_id);
} catch (const out_of_range&) { } catch (const out_of_range&) {
// create_common_item throws this when it doesn't want to make an item // create_common_item throws this when it doesn't want to make an item
+1 -1
View File
@@ -919,7 +919,7 @@ void send_join_game_t(shared_ptr<Client> c, shared_ptr<Lobby> l) {
cmd.event = l->event; cmd.event = l->event;
cmd.section_id = l->section_id; cmd.section_id = l->section_id;
cmd.challenge_mode = (l->mode == 2) ? 1 : 0; 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.episode = l->episode;
cmd.unused2 = 0x01; cmd.unused2 = 0x01;
cmd.solo_mode = (l->mode == 3); cmd.solo_mode = (l->mode == 3);
+1 -1
View File
@@ -56,7 +56,7 @@ struct ServerState {
std::shared_ptr<const QuestIndex> quest_index; std::shared_ptr<const QuestIndex> quest_index;
std::shared_ptr<const LevelTable> level_table; std::shared_ptr<const LevelTable> level_table;
std::shared_ptr<const BattleParamTable> battle_params; std::shared_ptr<const BattleParamTable> battle_params;
std::shared_ptr<const CommonItemCreator> common_item_creator; std::shared_ptr<const CommonItemData> common_item_data;
std::shared_ptr<LicenseManager> license_manager; std::shared_ptr<LicenseManager> license_manager;