add $rand command
This commit is contained in:
@@ -89,6 +89,7 @@ Some commands only work on the game server and not on the proxy server. The chat
|
||||
* Personal state commands
|
||||
* `$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.
|
||||
* `$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)
|
||||
* `$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.
|
||||
|
||||
@@ -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,
|
||||
shared_ptr<Client> c, const std::u16string& args) {
|
||||
check_is_game(l, true);
|
||||
@@ -828,6 +852,7 @@ static const unordered_map<u16string, ChatCommandDefinition> 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 <name-or-number>"}},
|
||||
// TODO: implement this on proxy server
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
+57
-46
@@ -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.
|
||||
@@ -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.
|
||||
CommonItemCreator::CommonItemCreator(
|
||||
const vector<uint32_t>& enemy_item_categories,
|
||||
const vector<uint32_t>& box_item_categories,
|
||||
const vector<vector<uint8_t>>& unit_types) :
|
||||
enemy_item_categories(enemy_item_categories),
|
||||
box_item_categories(box_item_categories),
|
||||
unit_types(unit_types) {
|
||||
CommonItemData::CommonItemData(
|
||||
vector<uint32_t>&& enemy_item_categories,
|
||||
vector<uint32_t>&& box_item_categories,
|
||||
vector<vector<uint8_t>>&& 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<uint32_t>();
|
||||
CommonItemCreator::CommonItemCreator(
|
||||
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;
|
||||
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:
|
||||
|
||||
+17
-5
@@ -3,20 +3,32 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <random>
|
||||
|
||||
#include "Lobby.hh"
|
||||
#include "Client.hh"
|
||||
|
||||
|
||||
|
||||
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> box_item_categories;
|
||||
std::vector<std::vector<uint8_t>> unit_types;
|
||||
|
||||
CommonItemCreator(const std::vector<uint32_t>& enemy_item_categories,
|
||||
const std::vector<uint32_t>& box_item_categories,
|
||||
const std::vector<std::vector<uint8_t>>& unit_types);
|
||||
CommonItemData(
|
||||
std::vector<uint32_t>&& enemy_item_categories,
|
||||
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;
|
||||
ItemData create_drop_item(bool is_box, uint8_t episode, uint8_t difficulty,
|
||||
|
||||
+2
-1
@@ -23,7 +23,8 @@ Lobby::Lobby(uint32_t id)
|
||||
episode(1),
|
||||
difficulty(0),
|
||||
mode(0),
|
||||
rare_seed(random_object<uint32_t>()),
|
||||
random_seed(random_object<uint32_t>()),
|
||||
random(new mt19937(this->random_seed)),
|
||||
event(0),
|
||||
block(0),
|
||||
type(0),
|
||||
|
||||
+6
-3
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <random>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -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<std::mt19937> random;
|
||||
std::shared_ptr<const CommonItemCreator> common_item_creator;
|
||||
|
||||
// lobby stuff
|
||||
uint8_t event;
|
||||
|
||||
+9
-7
@@ -74,14 +74,16 @@ void populate_state_from_config(shared_ptr<ServerState> s,
|
||||
|
||||
s->set_port_configuration(parse_port_configuration(d.at("PortConfiguration")));
|
||||
|
||||
auto enemy_categories = parse_int_vector<uint32_t>(d.at("CommonItemDropRates-Enemy"));
|
||||
auto box_categories = parse_int_vector<uint32_t>(d.at("CommonItemDropRates-Box"));
|
||||
vector<vector<uint8_t>> unit_types;
|
||||
for (const auto& item : d.at("CommonUnitTypes")->as_list()) {
|
||||
unit_types.emplace_back(parse_int_vector<uint8_t>(item));
|
||||
{
|
||||
auto enemy_categories = parse_int_vector<uint32_t>(d.at("CommonItemDropRates-Enemy"));
|
||||
auto box_categories = parse_int_vector<uint32_t>(d.at("CommonItemDropRates-Box"));
|
||||
vector<vector<uint8_t>> unit_types;
|
||||
for (const auto& item : d.at("CommonUnitTypes")->as_list()) {
|
||||
unit_types.emplace_back(parse_int_vector<uint8_t>(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 {
|
||||
|
||||
@@ -841,6 +841,10 @@ static HandlerResult process_server_64(shared_ptr<ServerState>,
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+2
-2
@@ -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<uint32_t>() < rate);
|
||||
return (random() < rate);
|
||||
}
|
||||
|
||||
+3
-1
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <random>
|
||||
|
||||
|
||||
|
||||
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);
|
||||
|
||||
@@ -1865,6 +1865,12 @@ shared_ptr<Lobby> create_game_generic(shared_ptr<ServerState> 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<Lobby> create_game_generic(shared_ptr<ServerState> 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++) {
|
||||
|
||||
+21
-12
@@ -544,13 +544,16 @@ static void process_subcommand_use_item(shared_ptr<ServerState>,
|
||||
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,
|
||||
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_ptr<ServerStat
|
||||
while (c->game_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<ServerState>,
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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,
|
||||
const string& data) {
|
||||
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()) {
|
||||
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<ServerState> 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<ServerState> 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<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,
|
||||
const string& data) {
|
||||
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()) {
|
||||
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<ServerState> 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<ServerState> 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
|
||||
|
||||
+1
-1
@@ -919,7 +919,7 @@ void send_join_game_t(shared_ptr<Client> c, shared_ptr<Lobby> 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);
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ struct ServerState {
|
||||
std::shared_ptr<const QuestIndex> quest_index;
|
||||
std::shared_ptr<const LevelTable> level_table;
|
||||
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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user