switch rare drops to stacked space logic
This commit is contained in:
@@ -1826,13 +1826,13 @@ struct C_Login_DC_PC_GC_9D {
|
||||
/* 08 */ be_uint64_t hardware_id;
|
||||
/* 10 */ le_uint32_t sub_version = 0;
|
||||
/* 14 */ uint8_t is_extended = 0; // If 1, structure has extended format
|
||||
/* 15 */ Language language = Language::JAPANESE; // 0 = JP, 1 = EN, 2 = DE, 3 = FR, 4 = ES
|
||||
/* 15 */ Language language = Language::JAPANESE;
|
||||
/* 16 */ parray<uint8_t, 0x2> unused3; // Always zeroes
|
||||
/* 18 */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
|
||||
/* 28 */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
|
||||
/* 38 */ pstring<TextEncoding::ASCII, 0x10> serial_number; // On XB, this is the XBL gamertag
|
||||
/* 48 */ pstring<TextEncoding::ASCII, 0x10> access_key; // On XB, this is the XBL user ID
|
||||
/* 58 */ pstring<TextEncoding::ASCII, 0x30> serial_number2; // On DCv2, this is the hardware ID; on XB, this is the XBL gamertag
|
||||
/* 58 */ pstring<TextEncoding::ASCII, 0x30> serial_number2; // DCv2: hardware ID; XB: XBL gamertag
|
||||
/* 88 */ pstring<TextEncoding::ASCII, 0x30> access_key2; // On XB, this is the XBL user ID
|
||||
/* B8 */ pstring<TextEncoding::ASCII, 0x10> login_character_name;
|
||||
/* C8 */
|
||||
|
||||
+41
-44
@@ -38,6 +38,7 @@ ItemCreator::ItemCreator(
|
||||
shared_ptr<const BattleRules> restrictions)
|
||||
: log(std::format("[ItemCreator:{}/{}/{}/{}] ", phosg::name_for_enum(stack_limits->version), abbreviation_for_mode(mode), abbreviation_for_difficulty(difficulty), section_id), lobby_log.min_level),
|
||||
logic_version(stack_limits->version),
|
||||
is_legacy_replay(false),
|
||||
stack_limits(stack_limits),
|
||||
mode(mode),
|
||||
difficulty(difficulty),
|
||||
@@ -286,30 +287,48 @@ ItemCreator::DropResult ItemCreator::on_monster_item_drop(EnemyType enemy_type,
|
||||
}
|
||||
}
|
||||
|
||||
ItemData ItemCreator::check_rare_specs_and_create_rare_item(
|
||||
const std::vector<RareItemSet::ExpandedDrop>& specs, uint8_t area, bool force_rare) {
|
||||
if (specs.empty()) {
|
||||
return ItemData();
|
||||
}
|
||||
|
||||
// This logic differs from the original client logic. This logic "stacks" all rare rates into a single probability
|
||||
// space, whereas the original client logic chooses a new random number for each rare spec that it checks. The
|
||||
// stacking logic makes the order of specs irrelevant, whereas the original client logic means that later specs are
|
||||
// actually more rare than they should be. In the original client, this only matters for boxes, because enemies could
|
||||
// not have multiple specs. Also, the original code uses 0xFFFFFFFF as the maximum here; we use 0x100000000 instead,
|
||||
// which makes all rare items SLIGHTLY more rare.
|
||||
int64_t det = force_rare ? 0 : this->rand_int(0x100000000);
|
||||
if (this->is_legacy_replay) {
|
||||
// For some old tests, we waste a few replay values because they used the old (non-stacked) logic. New tests should
|
||||
// not use this codepath.
|
||||
for (size_t z = 1; z < specs.size(); z++) {
|
||||
this->rand_int(0x100000000);
|
||||
}
|
||||
}
|
||||
this->log.info_f("{} specs to check with det={:08X}", specs.size(), det);
|
||||
for (const auto& spec : specs) {
|
||||
if (this->log.should_log(phosg::LogLevel::L_INFO)) {
|
||||
this->log.info_f("Checking spec {:08X} => {} with det={:08X}", spec.probability, spec.data.hex(), det);
|
||||
}
|
||||
det -= spec.probability;
|
||||
if (det < 0) {
|
||||
return this->create_rare_item(spec.data, area);
|
||||
}
|
||||
}
|
||||
return ItemData();
|
||||
}
|
||||
|
||||
ItemData ItemCreator::check_rare_specs_and_create_rare_box_item(uint8_t area, bool force_rare) {
|
||||
ItemData item;
|
||||
if (!this->are_rare_drops_allowed()) {
|
||||
return item;
|
||||
return ItemData();
|
||||
}
|
||||
|
||||
uint8_t table_index = this->table_index_for_area(area);
|
||||
Episode episode = episode_for_area(area);
|
||||
auto rare_specs = this->rare_item_set->get_box_specs(this->mode, episode, this->difficulty, this->section_id, table_index);
|
||||
for (const auto& spec : rare_specs) {
|
||||
item = this->check_rate_and_create_rare_item(spec, area, force_rare);
|
||||
if (!item.empty()) {
|
||||
if (this->log.should_log(phosg::LogLevel::L_INFO)) {
|
||||
auto hex = spec.data.hex();
|
||||
this->log.info_f("Box spec {:08X} produced item {}", spec.probability, hex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (this->log.should_log(phosg::LogLevel::L_INFO)) {
|
||||
auto hex = spec.data.hex();
|
||||
this->log.info_f("Box spec {:08X} did not produce item {}", spec.probability, hex);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
auto specs = this->rare_item_set->get_box_specs(this->mode, episode, this->difficulty, this->section_id, table_index);
|
||||
return this->check_rare_specs_and_create_rare_item(specs, area, force_rare);
|
||||
}
|
||||
|
||||
uint32_t ItemCreator::rand_int(uint64_t max) {
|
||||
@@ -351,35 +370,13 @@ ItemData ItemCreator::check_rare_spec_and_create_rare_enemy_item(EnemyType enemy
|
||||
// can have multiple rare drops if JSONRareItemSet is used (the other RareItemSet implementations never return
|
||||
// multiple drops for an enemy type).
|
||||
Episode episode = episode_for_area(area);
|
||||
auto rare_specs = this->rare_item_set->get_enemy_specs(
|
||||
auto specs = this->rare_item_set->get_enemy_specs(
|
||||
this->mode, episode, this->difficulty, this->section_id, enemy_type);
|
||||
ItemData item;
|
||||
for (const auto& spec : rare_specs) {
|
||||
item = this->check_rate_and_create_rare_item(spec, area, force_rare);
|
||||
if (!item.empty()) {
|
||||
if (this->log.should_log(phosg::LogLevel::L_INFO)) {
|
||||
auto hex = spec.data.hex();
|
||||
this->log.info_f("Enemy spec {:08X} produced item {}", spec.probability, hex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (this->log.should_log(phosg::LogLevel::L_INFO)) {
|
||||
auto hex = spec.data.hex();
|
||||
this->log.info_f("Enemy spec {:08X} did not produce item {}", spec.probability, hex);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
return this->check_rare_specs_and_create_rare_item(specs, area, force_rare);
|
||||
}
|
||||
|
||||
ItemData ItemCreator::check_rate_and_create_rare_item(
|
||||
const RareItemSet::ExpandedDrop& drop, uint8_t area, bool force_rare) {
|
||||
// Note: The original code uses 0xFFFFFFFF as the maximum here. We use 0x100000000 instead, which makes all rare
|
||||
// items SLIGHTLY more rare.
|
||||
if (!force_rare && ((drop.probability == 0) || (this->rand_int(0x100000000) >= drop.probability))) {
|
||||
return ItemData();
|
||||
}
|
||||
|
||||
ItemData item = drop.data;
|
||||
ItemData ItemCreator::create_rare_item(const ItemData& drop_item, uint8_t area) {
|
||||
ItemData item = drop_item;
|
||||
if (item.can_be_encoded_in_rel_rare_table()) {
|
||||
switch (item.data1[0]) {
|
||||
case 0:
|
||||
|
||||
+8
-1
@@ -37,6 +37,10 @@ public:
|
||||
bool is_from_rare_table = false;
|
||||
};
|
||||
|
||||
inline void set_legacy_replay() {
|
||||
this->is_legacy_replay = true;
|
||||
}
|
||||
|
||||
DropResult on_monster_item_drop(EnemyType enemy_type, uint8_t area, bool force_rare);
|
||||
DropResult on_box_item_drop(uint8_t area, bool force_rare);
|
||||
// Note: param3-6 refer to the corresponding fields of the object definition
|
||||
@@ -70,6 +74,7 @@ private:
|
||||
|
||||
phosg::PrefixedLogger log;
|
||||
Version logic_version;
|
||||
bool is_legacy_replay;
|
||||
std::shared_ptr<const ItemData::StackLimits> stack_limits;
|
||||
GameMode mode;
|
||||
Difficulty difficulty;
|
||||
@@ -123,7 +128,9 @@ private:
|
||||
|
||||
ItemData check_rare_spec_and_create_rare_enemy_item(EnemyType enemy_type, uint8_t area, bool force_rare);
|
||||
ItemData check_rare_specs_and_create_rare_box_item(uint8_t area, bool force_rare);
|
||||
ItemData check_rate_and_create_rare_item(const RareItemSet::ExpandedDrop& drop, uint8_t area, bool force_rare);
|
||||
ItemData check_rare_specs_and_create_rare_item(
|
||||
const std::vector<RareItemSet::ExpandedDrop>& specs, uint8_t area, bool force_rare);
|
||||
ItemData create_rare_item(const ItemData& drop_item, uint8_t area);
|
||||
|
||||
void generate_rare_weapon_bonuses(ItemData& item, Episode episode, uint32_t random_sample);
|
||||
void deduplicate_weapon_bonuses(ItemData& item) const;
|
||||
|
||||
@@ -230,6 +230,9 @@ void Lobby::create_item_creator(Version logic_version) {
|
||||
effective_section_id,
|
||||
rand_crypt,
|
||||
this->quest ? this->quest->meta.battle_rules : nullptr);
|
||||
if (s->use_legacy_item_random_behavior) {
|
||||
this->item_creator->set_legacy_replay();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Lobby::effective_section_id() const {
|
||||
|
||||
@@ -365,6 +365,9 @@ ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log, boo
|
||||
if (line == "### use psov2 crypt") {
|
||||
this->state->use_psov2_rand_crypt = true;
|
||||
}
|
||||
if (line == "### use legacy item random behavior") {
|
||||
this->state->use_legacy_item_random_behavior = true;
|
||||
}
|
||||
if (line.starts_with("### cc ")) {
|
||||
// ### cc $<chat command>
|
||||
if (this->clients.size() != 1) {
|
||||
|
||||
@@ -157,6 +157,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
bool default_switch_assist_enabled = false;
|
||||
bool use_game_creator_section_id = false;
|
||||
bool use_psov2_rand_crypt = false; // Used in some tests
|
||||
bool use_legacy_item_random_behavior = false; // Used in some tests
|
||||
bool rare_notifs_enabled_for_client_drops = false;
|
||||
bool default_rare_notifs_enabled_v1_v2 = false;
|
||||
bool default_rare_notifs_enabled_v3_v4 = false;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
### use psov2 crypt
|
||||
### use legacy item random behavior
|
||||
I 35932 2025-05-22 21:56:44 - [C-1] Channel name updated: C-1 @ ip:10.37.129.2:49620
|
||||
I 35932 2025-05-22 21:56:44 - [C-1] Created
|
||||
I 35932 2025-05-22 21:56:44 - [GameServer] Client connected: C-1 via TG-9300-PC_V2-pc-game_server
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
### use psov2 crypt
|
||||
### use legacy item random behavior
|
||||
I 39471 2025-05-21 23:39:20 - [IPStackSimulator] Virtual network N-2 connected via T-IPS-5059
|
||||
I 39471 2025-05-21 23:39:20 - [IPStackSimulator] Client opened TCP connection 23232323238C062D (10.0.1.535:1581 -> 35.35.35.35:9100) (acked_server_seq=BD656BE7, next_client_seq=4F6748DF)
|
||||
I 39471 2025-05-21 23:39:20 - [C-9] Channel name updated: C-9 @ ipss:N-2:127.0.0.1:57072
|
||||
|
||||
Reference in New Issue
Block a user