describe how ep3 card drop rates actually work
This commit is contained in:
+65
-10
@@ -911,6 +911,57 @@ string string_for_range(const parray<be_uint32_t, 6>& range) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
string string_for_drop_rate(uint16_t drop_rate) {
|
||||
vector<string> tokens;
|
||||
switch (drop_rate % 10) {
|
||||
case 0:
|
||||
tokens.emplace_back("mode=ANY");
|
||||
break;
|
||||
case 1:
|
||||
tokens.emplace_back("mode=OFFLINE_STORY");
|
||||
break;
|
||||
case 2:
|
||||
tokens.emplace_back("mode=OFFLINE_FREE_BATTLE");
|
||||
break;
|
||||
case 3:
|
||||
tokens.emplace_back("mode=OFFLINE_FREE_BATTLE_PVP");
|
||||
break;
|
||||
case 4:
|
||||
tokens.emplace_back("mode=ONLINE");
|
||||
break;
|
||||
case 5:
|
||||
tokens.emplace_back("mode=TOURNAMENT");
|
||||
break;
|
||||
case 6:
|
||||
tokens.emplace_back("mode=FORBIDDEN");
|
||||
break;
|
||||
default:
|
||||
tokens.emplace_back("mode=__UNKNOWN__");
|
||||
}
|
||||
uint8_t environment_number = (drop_rate / 10) % 100;
|
||||
if (environment_number) {
|
||||
tokens.emplace_back(string_printf("environment_number=%02hhX", static_cast<uint8_t>(environment_number - 1)));
|
||||
} else {
|
||||
tokens.emplace_back("environment_number=ANY");
|
||||
}
|
||||
tokens.emplace_back(string_printf("rarity_class=%hhu", static_cast<uint8_t>((drop_rate / 1000) % 10)));
|
||||
switch ((drop_rate / 10000) % 10) {
|
||||
case 0:
|
||||
tokens.emplace_back("deck_type=ANY");
|
||||
break;
|
||||
case 1:
|
||||
tokens.emplace_back("deck_type=HUNTERS");
|
||||
break;
|
||||
case 2:
|
||||
tokens.emplace_back("deck_type=ARKZ");
|
||||
break;
|
||||
default:
|
||||
tokens.emplace_back("deck_type=__UNKNOWN__");
|
||||
}
|
||||
string description = join(tokens, ", ");
|
||||
return string_printf("[%hu: %s]", drop_rate, description.c_str());
|
||||
}
|
||||
|
||||
string CardDefinition::str(bool single_line) const {
|
||||
string type_str;
|
||||
try {
|
||||
@@ -956,6 +1007,9 @@ string CardDefinition::str(bool single_line) const {
|
||||
effects_str = " (none)";
|
||||
}
|
||||
|
||||
string drop0_str = string_for_drop_rate(this->drop_rates[0]);
|
||||
string drop1_str = string_for_drop_rate(this->drop_rates[1]);
|
||||
|
||||
if (single_line) {
|
||||
string range_str = string_for_range(this->range);
|
||||
return string_printf(
|
||||
@@ -963,7 +1017,7 @@ string CardDefinition::str(bool single_line) const {
|
||||
"cost=%hhX+%hhX target=%s range=%s assist_turns=%s cannot_move=%s "
|
||||
"cannot_attack=%s cannot_drop=%s hp=%s ap=%s tp=%s mv=%s left=%s right=%s "
|
||||
"top=%s a2=%04hX class=%s assist_effect=[%hu, %hu] "
|
||||
"drop_rates=[%hu, %hu] effects=[%s]]",
|
||||
"drop_rates=[%s, %s] effects=[%s]]",
|
||||
this->card_id.load(),
|
||||
this->en_name.data(),
|
||||
type_str.c_str(),
|
||||
@@ -988,8 +1042,8 @@ string CardDefinition::str(bool single_line) const {
|
||||
card_class_str.c_str(),
|
||||
this->assist_effect[0].load(),
|
||||
this->assist_effect[1].load(),
|
||||
this->drop_rates[0].load(),
|
||||
this->drop_rates[1].load(),
|
||||
drop0_str.c_str(),
|
||||
drop1_str.c_str(),
|
||||
effects_str.c_str());
|
||||
|
||||
} else { // Not single-line
|
||||
@@ -1024,7 +1078,7 @@ Card: %04" PRIX32 " \"%s\"\n\
|
||||
Left colors: %s; right colors: %s; top colors: %s\n\
|
||||
Unknown a2: %04hX\n\
|
||||
Assist effect: [%hu, %hu]\n\
|
||||
Drop rates: [%hu, %hu] (%s drop)\n\
|
||||
Drop rates: [%s, %s] (%s drop)\n\
|
||||
Effects:%s",
|
||||
this->card_id.load(),
|
||||
this->en_name.data(),
|
||||
@@ -1049,8 +1103,8 @@ Card: %04" PRIX32 " \"%s\"\n\
|
||||
this->unknown_a2.load(),
|
||||
this->assist_effect[0].load(),
|
||||
this->assist_effect[1].load(),
|
||||
this->drop_rates[0].load(),
|
||||
this->drop_rates[1].load(),
|
||||
drop0_str.c_str(),
|
||||
drop1_str.c_str(),
|
||||
this->cannot_drop ? "cannot" : "can",
|
||||
effects_str.c_str());
|
||||
}
|
||||
@@ -1429,8 +1483,9 @@ string MapDefinition::str(const CardIndex* card_index) const {
|
||||
lines.emplace_back(string_printf(" reward_cards[%02zu]: %04hX", z, card_id));
|
||||
}
|
||||
}
|
||||
lines.emplace_back(string_printf(" a9=[%08" PRIX32 " %08" PRIX32 " %04hX %04hX]",
|
||||
this->unknown_a9_a.load(), this->unknown_a9_b.load(), this->unknown_a9_c.load(), this->unknown_a9_d.load()));
|
||||
lines.emplace_back(string_printf(" level_overrides=[win=%" PRId32 ", loss=%" PRId32 "]",
|
||||
this->win_level_override.load(), this->loss_level_override.load()));
|
||||
lines.emplace_back(string_printf(" a9=[%04hX %04hX]", this->unknown_a9_c.load(), this->unknown_a9_d.load()));
|
||||
lines.emplace_back(string_printf(" a10=%02hhX", this->unknown_a10));
|
||||
lines.emplace_back(string_printf(" cyber_block_type=%02hhX", this->cyber_block_type));
|
||||
lines.emplace_back(string_printf(" a11=%02hhX%02hhX", this->unknown_a11[0], this->unknown_a11[1]));
|
||||
@@ -1553,8 +1608,8 @@ MapDefinitionTrial::MapDefinitionTrial(const MapDefinition& map)
|
||||
dispatch_message(map.dispatch_message),
|
||||
dialogue_sets(),
|
||||
reward_card_ids(map.reward_card_ids),
|
||||
unknown_a9_a(map.unknown_a9_a),
|
||||
unknown_a9_b(map.unknown_a9_b),
|
||||
win_level_override(map.win_level_override),
|
||||
loss_level_override(map.loss_level_override),
|
||||
unknown_a9_c(map.unknown_a9_c),
|
||||
unknown_a9_d(map.unknown_a9_d),
|
||||
unknown_a10(map.unknown_a10),
|
||||
|
||||
+70
-11
@@ -523,6 +523,7 @@ struct CardDefinition {
|
||||
// stored big-endian here, so there's a helper function (card_class()) that
|
||||
// returns a usable CardClass enum value.
|
||||
/* 0096 */ be_uint16_t be_card_class;
|
||||
|
||||
// The two fields of this array seem to always contain the same value, and
|
||||
// are always 0 for non-assist cards and nonzero for assists. Each assist
|
||||
// card has a unique value here and no effects, though the server ignores
|
||||
@@ -533,16 +534,69 @@ struct CardDefinition {
|
||||
// determine assist cards' effects (see e.g. Skip Draw / Skip Move, Dice
|
||||
// Fever / Dice Fever +, Reverse Card / Rich +).
|
||||
/* 0098 */ parray<be_uint16_t, 2> assist_effect;
|
||||
|
||||
// Drop rates are decimal-encoded with the following fields:
|
||||
// - rate % 10 (that is, the lowest decimal place) specifies the required game
|
||||
// mode. 0 means any mode, 1 means offline only, 2 means 1P free-battle, 3
|
||||
// means 2P+ free battle, 4 means story mode.
|
||||
// mode. 0 means any mode, 1 means offline story mode, 2 means 1P free
|
||||
// battle, 3 means 2P+ free battle (specifically, PvP - two humans vs. two
|
||||
// COMs counts as 1P free battle), 4 means online mode, 5 means tournament.
|
||||
// Some cards have this field set to 6, which isn't a valid game mode; it
|
||||
// seems Sega used this as a way to make sure the drop rate never applies.
|
||||
// - (rate / 10) % 100 (that is, the tens and hundreds decimal places) specify
|
||||
// something else, but it's not clear what exactly.
|
||||
// - rate / 1000 (the thousands decimal place) specifies the level class
|
||||
// required to get this drop.
|
||||
// - rate / 10000 (the ten-thousands decimal place) must be either 0, 1, or 2,
|
||||
// but it's not clear yet what each value means.
|
||||
// the environment number + 1. For example, if this field contains 5, then
|
||||
// this drop only applies if the battle took place at Molae Venti
|
||||
// (environment number 4). If this field is zero, the drop applies
|
||||
// regardless of where the battle took place.
|
||||
// - rate / 1000 (the thousands decimal place) specifies the rarity class.
|
||||
// This can be any number in the range [0, 9], and affects how likely the
|
||||
// card is to appear based on the player's level. See below for details.
|
||||
// - rate / 10000 (the ten-thousands decimal place) specifies if the drop rate
|
||||
// applies only if the player used a Hunters deck (1), only if they used an
|
||||
// Arkz deck (2), or if they used any deck (0).
|
||||
// When determining which cards to drop, the game first checks the drop rate
|
||||
// fields on all cards. For each drop rate that applies, the game adds the
|
||||
// card ID into an appropriate bucket based on the rarity class. (If both drop
|
||||
// rates for a card apply, the card ID is added twice.) The player's level
|
||||
// class is then computed according to the following table:
|
||||
// 1 2 3 4 5 6 7 8 9 10
|
||||
// CLvOff 1-2 3-4 5-9 10-14 15-19 20-25 26-29 30-39 40-49 50+
|
||||
// CLvOn 1-2 3-4 5-10 11-16 17-23 24-32 33-39 40-49 50-99 100+
|
||||
// For the purposes of this computation, the player's level is used by default
|
||||
// (CLvOn or CLvOff), but the map may override it - see win_level_override and
|
||||
// loss_level_override in MapDefinition. This specifies which row in the
|
||||
// following tables will be used.
|
||||
// Finally, cards are chosen from the buckets with a weighted distribution
|
||||
// according to these tables (row is player's level class, column is card's
|
||||
// rarity class):
|
||||
// Offline
|
||||
// 1 2 3 4 5 6 7 8 9 10
|
||||
// 1 => 8000 2000 50
|
||||
// 2 => 6000 3500 500 50
|
||||
// 3 => 4500 3500 1500 400 100
|
||||
// 4 => 3000 3000 2500 1000 450 50
|
||||
// 5 => 2000 2600 2750 2000 500 100 50
|
||||
// 6 => 1900 2200 2500 2100 830 350 100 20
|
||||
// 7 => 1900 2000 2000 2000 1000 500 500 100
|
||||
// 8 => 160000 160000 190000 190000 130000 100000 50000 19999 1
|
||||
// 9 => 120000 120000 150000 160000 150000 150000 100000 49989 10 1
|
||||
// 10 => 120000 120000 130000 150000 160000 150000 100000 69965 30 5
|
||||
// Online
|
||||
// 1 2 3 4 5 6 7 8 9 10
|
||||
// 1 => 8000 2000 50
|
||||
// 2 => 6000 3500 500 20
|
||||
// 3 => 4500 4000 1500 200
|
||||
// 4 => 3500 3500 2300 700 20
|
||||
// 5 => 2700 2800 2500 1500 500 10
|
||||
// 6 => 2300 2300 2300 1900 900 300 1
|
||||
// 7 => 1995 2100 2100 2100 1000 700 5
|
||||
// 8 => 1789 2100 2100 2100 1100 800 10 1
|
||||
// 9 => 14620 20000 21000 22000 13000 9000 300 80
|
||||
// 10 => 133997 190000 200000 200000 150000 120000 5000 1000 2 1
|
||||
// These values are all relative to other values in the same row. For example,
|
||||
// if your character is in level class 1, you'll get cards of rarity class 1
|
||||
// about 80% of the time, cards of rarity class 2 about 20% of the time, and
|
||||
// cards of rarity class 3 about 0.5% of the time. (The actual probabilities
|
||||
// are 8000/10050, 2000/10050, and 50/10050.)
|
||||
// The drop rates are completely ignored if any of the following are true
|
||||
// (which means the card can never be found in a normal post-battle draw):
|
||||
// - type is SC_HUNTERS or SC_ARKZ
|
||||
@@ -551,6 +605,7 @@ struct CardDefinition {
|
||||
// - cannot_drop is 1 (specifically 1; other nonzero values here don't
|
||||
// prevent the card from appearing in post-battle draws)
|
||||
/* 009C */ parray<be_uint16_t, 2> drop_rates;
|
||||
|
||||
/* 00A0 */ ptext<char, 0x14> en_name;
|
||||
/* 00B4 */ ptext<char, 0x0B> jp_short_name;
|
||||
/* 00BF */ ptext<char, 0x08> en_short_name;
|
||||
@@ -913,8 +968,12 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
|
||||
/* 59B0 */ parray<be_uint16_t, 0x10> reward_card_ids;
|
||||
|
||||
/* 59D0 */ be_uint32_t unknown_a9_a;
|
||||
/* 59D4 */ be_uint32_t unknown_a9_b;
|
||||
// These fields appear to be used for the purpose of determining cards to drop
|
||||
// after the battle is complete. If either is negative, the player's actual
|
||||
// CLv is used instead.
|
||||
/* 59D0 */ be_int32_t win_level_override;
|
||||
/* 59D4 */ be_int32_t loss_level_override;
|
||||
|
||||
/* 59D8 */ be_uint16_t unknown_a9_c;
|
||||
/* 59DA */ be_uint16_t unknown_a9_d;
|
||||
|
||||
@@ -1013,8 +1072,8 @@ struct MapDefinitionTrial {
|
||||
/* 2758 */ ptext<char, 0x190> dispatch_message;
|
||||
/* 28E8 */ parray<parray<MapDefinition::DialogueSet, 8>, 3> dialogue_sets;
|
||||
/* 4148 */ parray<be_uint16_t, 0x10> reward_card_ids;
|
||||
/* 4168 */ be_uint32_t unknown_a9_a;
|
||||
/* 416C */ be_uint32_t unknown_a9_b;
|
||||
/* 4168 */ be_uint32_t win_level_override;
|
||||
/* 416C */ be_uint32_t loss_level_override;
|
||||
/* 4170 */ be_uint16_t unknown_a9_c;
|
||||
/* 4172 */ be_uint16_t unknown_a9_d;
|
||||
/* 4174 */ uint8_t unknown_a10;
|
||||
|
||||
Reference in New Issue
Block a user