implement 6xE2 subcommand

This commit is contained in:
Martin Michelsen
2023-12-20 14:59:48 -08:00
parent f048a4f5fb
commit 82aaf4cd34
8 changed files with 217 additions and 23 deletions
+15 -8
View File
@@ -5647,9 +5647,9 @@ struct G_EnemyEXPRequest_BB_6xC8 {
parray<uint8_t, 3> unused; parray<uint8_t, 3> unused;
} __packed__; } __packed__;
// 6xC9: Request meseta reward from quest (BB; handled by server) // 6xC9: Adjust player Meseta (BB; handled by server)
struct G_MesetaRewardRequest_BB_6xC9 { struct G_AdjustPlayerMeseta_BB_6xC9 {
G_UnusedHeader header; G_UnusedHeader header;
le_int32_t amount = 0; le_int32_t amount = 0;
} __packed__; } __packed__;
@@ -5866,19 +5866,26 @@ struct G_ExchangePhotonTickets_BB_6xE1 {
le_uint16_t unknown_a5 = 0; // argsA[4] le_uint16_t unknown_a5 = 0; // argsA[4]
} __packed__; } __packed__;
// 6xE2: Coren actions (BB) // 6xE2: Get Meseta slot prize (BB)
// The client sends this when it executes an F960 quest opcode. // The client sends this when it executes an F960 quest opcode.
struct G_CorenActions_BB_6xE2 { struct G_GetMesetaSlotPrize_BB_6xE2 {
G_ClientIDHeader header; G_ClientIDHeader header;
parray<uint8_t, 12> unknown_a1; // TODO: There might be uint16_ts and uint32_ts in here. uint8_t result_tier; // This contains the argument value from the F960 opcode
uint8_t floor;
uint8_t unknown_a2;
uint8_t unused;
le_float x; // TODO: Verify this guess
le_float z; // TODO: Verify this guess
} __packed__; } __packed__;
// 6xE3: Coren actions result (BB) // 6xE3: Set Meseta slot prize result (BB)
// The client only uses this to populate the <meseta_slot_prize> quest text
// replacement token.
struct G_CorenActionsResult_BB_6xE3 { struct G_SetMesetaSlotPrizeResult_BB_6xE3 {
G_ClientIDHeader header; G_ClientIDHeader header;
ItemData item_data; ItemData item;
} __packed__; } __packed__;
// 6xE4: Invalid subcommand // 6xE4: Invalid subcommand
+4 -2
View File
@@ -390,9 +390,11 @@ ItemData ItemNameIndex::parse_item_description(Version version, const std::strin
} }
} catch (const exception& ed) { } catch (const exception& ed) {
if (strcmp(e1.what(), e2.what())) { if (strcmp(e1.what(), e2.what())) {
throw runtime_error(string_printf("cannot parse item description (as text 1: %s) (as text 2: %s) (as data: %s)", e1.what(), e2.what(), ed.what())); throw runtime_error(string_printf("cannot parse item description \"%s\" in %s (as text 1: %s) (as text 2: %s) (as data: %s)",
desc.c_str(), name_for_enum(version), e1.what(), e2.what(), ed.what()));
} else { } else {
throw runtime_error(string_printf("cannot parse item description (as text: %s) (as data: %s)", e1.what(), ed.what())); throw runtime_error(string_printf("cannot parse item description \"%s\" in %s (as text: %s) (as data: %s)",
desc.c_str(), name_for_enum(version), e1.what(), ed.what()));
} }
} }
} }
+56 -4
View File
@@ -2082,8 +2082,8 @@ static void on_enemy_exp_request_bb(shared_ptr<Client> c, uint8_t, uint8_t, cons
} }
} }
void on_meseta_reward_request_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) { void on_adjust_player_meseta_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
const auto& cmd = check_size_t<G_MesetaRewardRequest_BB_6xC9>(data, size); const auto& cmd = check_size_t<G_AdjustPlayerMeseta_BB_6xC9>(data, size);
auto p = c->character(); auto p = c->character();
if (cmd.amount < 0) { if (cmd.amount < 0) {
@@ -2692,6 +2692,58 @@ static void on_quest_F95F_result_bb(shared_ptr<Client> c, uint8_t, uint8_t, cons
} }
} }
static void on_quest_F960_result_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
auto l = c->require_lobby();
if (l->is_game() && (l->base_version == Version::BB_V4)) {
const auto& cmd = check_size_t<G_GetMesetaSlotPrize_BB_6xE2>(data, size);
auto s = c->require_server_state();
auto p = c->character();
time_t t_secs = now() / 1000000;
struct tm t_parsed;
gmtime_r(&t_secs, &t_parsed);
size_t weekday = t_parsed.tm_wday;
ItemData item;
for (size_t num_failures = 0; num_failures <= cmd.result_tier; num_failures++) {
size_t tier = cmd.result_tier - num_failures;
const auto& results = s->quest_F960_success_results.at(tier);
uint64_t probability = results.base_probability + num_failures * results.probability_upgrade;
if (random_object<uint32_t>() <= probability) {
c->log.info("Tier %zu yielded a prize", tier);
const auto& result_items = results.results.at(weekday);
item = result_items[random_object<uint32_t>() % result_items.size()];
break;
} else {
c->log.info("Tier %zu did not yield a prize", tier);
}
}
if (item.empty()) {
c->log.info("Choosing result from failure tier");
const auto& result_items = s->quest_F960_failure_results.results.at(weekday);
item = result_items[random_object<uint32_t>() % result_items.size()];
}
if (item.empty()) {
throw runtime_error("no item produced, even from failure tier");
}
// The client sends a 6xC9 to remove Meseta before sending 6xE2, so we don't
// have to deal with Meseta here.
item.id = l->generate_item_id(c->lobby_client_id);
p->add_item(item);
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
G_SetMesetaSlotPrizeResult_BB_6xE3 cmd_6xE3 = {{0xE3, sizeof(G_SetMesetaSlotPrizeResult_BB_6xE3) >> 2, 0x0000}, item};
send_command_t(c, 0x60, 0x00, cmd_6xE3);
if (c->log.should_log(LogLevel::INFO)) {
string name = s->item_name_index->describe_item(c->version(), item);
c->log.info("Awarded item %s", name.c_str());
}
}
}
static void on_momoka_item_exchange_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) { static void on_momoka_item_exchange_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->is_game() && (l->base_version == Version::BB_V4) && l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) { if (l->is_game() && (l->base_version == Version::BB_V4) && l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) {
@@ -2984,7 +3036,7 @@ const SubcommandDefinition subcommand_definitions[0x100] = {
/* 6xC6 */ {0x00, 0x00, 0xC6, on_steal_exp_bb}, /* 6xC6 */ {0x00, 0x00, 0xC6, on_steal_exp_bb},
/* 6xC7 */ {0x00, 0x00, 0xC7, on_charge_attack_bb}, /* 6xC7 */ {0x00, 0x00, 0xC7, on_charge_attack_bb},
/* 6xC8 */ {0x00, 0x00, 0xC8, on_enemy_exp_request_bb}, /* 6xC8 */ {0x00, 0x00, 0xC8, on_enemy_exp_request_bb},
/* 6xC9 */ {0x00, 0x00, 0xC9, on_meseta_reward_request_bb}, /* 6xC9 */ {0x00, 0x00, 0xC9, on_adjust_player_meseta_bb},
/* 6xCA */ {0x00, 0x00, 0xCA, on_item_reward_request_bb}, /* 6xCA */ {0x00, 0x00, 0xCA, on_item_reward_request_bb},
/* 6xCB */ {0x00, 0x00, 0xCB, on_transfer_item_via_mail_message_bb}, /* 6xCB */ {0x00, 0x00, 0xCB, on_transfer_item_via_mail_message_bb},
/* 6xCC */ {0x00, 0x00, 0xCC, on_exchange_item_for_team_points_bb}, /* 6xCC */ {0x00, 0x00, 0xCC, on_exchange_item_for_team_points_bb},
@@ -3009,7 +3061,7 @@ const SubcommandDefinition subcommand_definitions[0x100] = {
/* 6xDF */ {0x00, 0x00, 0xDF, on_photon_crystal_exchange_bb}, /* 6xDF */ {0x00, 0x00, 0xDF, on_photon_crystal_exchange_bb},
/* 6xE0 */ {0x00, 0x00, 0xE0, on_quest_F95E_result_bb}, /* 6xE0 */ {0x00, 0x00, 0xE0, on_quest_F95E_result_bb},
/* 6xE1 */ {0x00, 0x00, 0xE1, on_quest_F95F_result_bb}, /* 6xE1 */ {0x00, 0x00, 0xE1, on_quest_F95F_result_bb},
/* 6xE2 */ {0x00, 0x00, 0xE2, nullptr}, /* 6xE2 */ {0x00, 0x00, 0xE2, on_quest_F960_result_bb},
/* 6xE3 */ {0x00, 0x00, 0xE3, nullptr}, /* 6xE3 */ {0x00, 0x00, 0xE3, nullptr},
/* 6xE4 */ {0x00, 0x00, 0xE4, nullptr}, /* 6xE4 */ {0x00, 0x00, 0xE4, nullptr},
/* 6xE5 */ {0x00, 0x00, 0xE5, nullptr}, /* 6xE5 */ {0x00, 0x00, 0xE5, nullptr},
+1
View File
@@ -297,6 +297,7 @@ Proxy session commands:\n\
} else if (type == "level-table") { } else if (type == "level-table") {
this->state->load_level_table(); this->state->load_level_table();
} else if (type == "item-tables") { } else if (type == "item-tables") {
this->state->load_item_name_index();
this->state->load_item_tables(); this->state->load_item_tables();
} else if (type == "word-select") { } else if (type == "word-select") {
this->state->load_word_select_table(); this->state->load_word_select_table();
+27 -7
View File
@@ -17,6 +17,18 @@
using namespace std; using namespace std;
ServerState::QuestF960Result::QuestF960Result(const JSON& json, std::shared_ptr<const ItemNameIndex> name_index) {
static const array<string, 7> day_names = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
this->meseta_cost = json.get_int("MesetaCost", 0);
this->base_probability = json.get_int("BaseProbability", 0);
this->probability_upgrade = json.get_int("ProbabilityUpgrade", 0);
for (size_t day = 0; day < 7; day++) {
for (const auto& item_it : json.get_list(day_names[day])) {
this->results[day].emplace_back(name_index->parse_item_description(Version::BB_V4, item_it->as_string()));
}
}
}
ServerState::ServerState(shared_ptr<struct event_base> base, const string& config_filename, bool is_replay) ServerState::ServerState(shared_ptr<struct event_base> base, const string& config_filename, bool is_replay)
: base(base), : base(base),
config_filename(config_filename), config_filename(config_filename),
@@ -125,6 +137,7 @@ void ServerState::init() {
// Load all the necessary data // Load all the necessary data
auto config = this->load_config(); auto config = this->load_config();
this->collect_network_addresses(); this->collect_network_addresses();
this->load_item_name_index();
this->parse_config(config, false); this->parse_config(config, false);
this->load_bb_private_keys(); this->load_bb_private_keys();
this->load_licenses(); this->load_licenses();
@@ -806,8 +819,7 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
for (const auto& difficulty_it : type_it->as_list()) { for (const auto& difficulty_it : type_it->as_list()) {
auto& difficulty_res = type_res.emplace_back(); auto& difficulty_res = type_res.emplace_back();
for (const auto& item_it : difficulty_it->as_list()) { for (const auto& item_it : difficulty_it->as_list()) {
string data = parse_data_string(item_it->as_string()); difficulty_res.emplace_back(this->item_name_index->parse_item_description(Version::BB_V4, item_it->as_string()));
difficulty_res.emplace_back(ItemData::from_data(data));
} }
} }
} }
@@ -818,16 +830,22 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
for (const auto& it : json.get_list("QuestF95FResultItems")) { for (const auto& it : json.get_list("QuestF95FResultItems")) {
auto& list = it->as_list(); auto& list = it->as_list();
size_t price = list.at(0)->as_int(); size_t price = list.at(0)->as_int();
string data = parse_data_string(list.at(1)->as_string()); this->quest_F95F_results.emplace_back(make_pair(price, this->item_name_index->parse_item_description(Version::BB_V4, list.at(1)->as_string())));
this->quest_F95F_results.emplace_back(make_pair(price, ItemData::from_data(data))); }
} catch (const out_of_range&) {
}
try {
this->quest_F960_success_results.clear();
this->quest_F960_failure_results = QuestF960Result(json.at("QuestF960FailureResultItems"), this->item_name_index);
for (const auto& it : json.get_list("QuestF960SuccessResultItems")) {
this->quest_F960_success_results.emplace_back(*it, this->item_name_index);
} }
} catch (const out_of_range&) { } catch (const out_of_range&) {
} }
try { try {
this->secret_lottery_results.clear(); this->secret_lottery_results.clear();
for (const auto& it : json.get_list("SecretLotteryResultItems")) { for (const auto& it : json.get_list("SecretLotteryResultItems")) {
string data = parse_data_string(it->as_string()); this->secret_lottery_results.emplace_back(this->item_name_index->parse_item_description(Version::BB_V4, it->as_string()));
this->secret_lottery_results.emplace_back(ItemData::from_data(data));
} }
} catch (const out_of_range&) { } catch (const out_of_range&) {
} }
@@ -1086,13 +1104,15 @@ void ServerState::load_word_select_table() {
this->word_select_table = make_shared<WordSelectTable>(JSON::parse(load_file("system/word-select-table.json"))); this->word_select_table = make_shared<WordSelectTable>(JSON::parse(load_file("system/word-select-table.json")));
} }
void ServerState::load_item_tables() { void ServerState::load_item_name_index() {
config_log.info("Loading item name index"); config_log.info("Loading item name index");
this->item_name_index = make_shared<ItemNameIndex>( this->item_name_index = make_shared<ItemNameIndex>(
JSON::parse(load_file("system/item-tables/names-v2.json")), JSON::parse(load_file("system/item-tables/names-v2.json")),
JSON::parse(load_file("system/item-tables/names-v3.json")), JSON::parse(load_file("system/item-tables/names-v3.json")),
JSON::parse(load_file("system/item-tables/names-v4.json"))); JSON::parse(load_file("system/item-tables/names-v4.json")));
}
void ServerState::load_item_tables() {
config_log.info("Loading rare item sets"); config_log.info("Loading rare item sets");
unordered_map<string, shared_ptr<const RareItemSet>> new_rare_item_sets; unordered_map<string, shared_ptr<const RareItemSet>> new_rare_item_sets;
for (const auto& filename : list_directory_sorted("system/item-tables")) { for (const auto& filename : list_directory_sorted("system/item-tables")) {
+13
View File
@@ -141,9 +141,21 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::shared_ptr<const Map::RareEnemyRates> rare_enemy_rates_challenge; std::shared_ptr<const Map::RareEnemyRates> rare_enemy_rates_challenge;
std::array<std::array<size_t, 4>, 3> min_levels_v4; // Indexed as [episode][difficulty] std::array<std::array<size_t, 4>, 3> min_levels_v4; // Indexed as [episode][difficulty]
struct QuestF960Result {
uint32_t meseta_cost = 0;
uint32_t base_probability = 0;
uint32_t probability_upgrade = 0;
std::array<std::vector<ItemData>, 7> results;
QuestF960Result() = default;
QuestF960Result(const JSON& json, std::shared_ptr<const ItemNameIndex> name_index);
};
// Indexed as [type][difficulty][random_choice] // Indexed as [type][difficulty][random_choice]
std::vector<std::vector<std::vector<ItemData>>> quest_F95E_results; std::vector<std::vector<std::vector<ItemData>>> quest_F95E_results;
std::vector<std::pair<size_t, ItemData>> quest_F95F_results; // [(num_photon_tickets, item)] std::vector<std::pair<size_t, ItemData>> quest_F95F_results; // [(num_photon_tickets, item)]
std::vector<QuestF960Result> quest_F960_success_results;
QuestF960Result quest_F960_failure_results;
std::vector<ItemData> secret_lottery_results; std::vector<ItemData> secret_lottery_results;
uint16_t bb_global_exp_multiplier; uint16_t bb_global_exp_multiplier;
@@ -268,6 +280,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
void load_patch_indexes(); void load_patch_indexes();
void load_battle_params(); void load_battle_params();
void load_level_table(); void load_level_table();
void load_item_name_index();
void load_item_tables(); void load_item_tables();
void load_word_select_table(); void load_word_select_table();
void load_ep3_data(); void load_ep3_data();
+55 -1
View File
@@ -552,7 +552,7 @@
// by type (which is the third argument to the opcode). The entries here are // by type (which is the third argument to the opcode). The entries here are
// [num_photon_tickets, item_hex]. // [num_photon_tickets, item_hex].
"QuestF95FResultItems": [ "QuestF95FResultItems": [
[0, ""], // Unused [0, "000100"], // Unused
[10, "00D500"], [10, "00D500"],
[15, "000A07"], [15, "000A07"],
[20, "010157"], [20, "010157"],
@@ -567,6 +567,60 @@
"010132", "010133", "010221", "010224", "010229", "01022B", "010235", "010132", "010133", "010221", "010224", "010229", "01022B", "010235",
"031000", "031000",
], ],
// Result items for Coren (quest opcodes F960/F961). Indexed by prize_tier.
// When a prize is requested, the server chooses a random number and checks it
// against the tier's probability. If the check passes, one of the items for
// the current weekday is chosen uniformly at random and given to the player.
// If the check fails, the next lower tier is checked in a similar manner,
// but uses BaseProbability + ProbabilityUpgrade. If that check fails, the
// next lower tier is checked, with 2x ProbabilityUpgrade, and so on. If no
// tiers produce an item, one of the QuestF960FailureResultItems is given.
"QuestF960SuccessResultItems": [
{
"MesetaCost": 1000,
"BaseProbability": 0x0A3D70A3, // 4%
"ProbabilityUpgrade": 0x0A3D70A3, // 4%
"Sunday": ["God/Power", "Cure/Poison", "Cure/Paralysis", "Cure/Slow", "Cure/Confuse", "Cure/Freeze", "Cure/Shock", "Tablet"],
"Monday": ["Three Seals", "God/Mind", "God/Arm", "Hero/Ability", "HP/Revival", "PB/Create", "Devil/Battle", "Cure/Slow"],
"Tuesday": ["God/HP", "God/Body", "PB/Create", "Cure/Poison", "Cure/Paralysis", "Cure/Freeze"],
"Wednesday": ["God/Legs", "Hero/Ability", "TP/Revival", "Devil/Battle", "Cure/Slow", "Tablet"],
"Thursday": ["God/TP", "Hero/Ability", "HP/Revival", "God/Technique", "Cure/Shock"],
"Friday": ["God/Luck", "TP/Revival", "PB/Create", "Devil/Battle", "Cure/Paralysis", "Cure/Slow", "Cure/Shock", "Tablet"],
"Saturday": ["Three Seals", "Hero/Ability", "God/Ability", "HP/Revival", "PB/Create", "Cure/Poison", "Cure/Paralysis", "Cure/Freeze"],
}, {
"MesetaCost": 10000,
"BaseProbability": 0x0A3D70A3, // 4%
"ProbabilityUpgrade": 0x0A3D70A3, // 4%
"Sunday": ["Kaladbolg", "Durandal", "Blade Dance", "M&A60 Vise", "H&S25 Justice", "L&K14 Combat", "Club of Laconium", "Photon Claw", "Silence Claw", "Stag Cutlery", "Holy Ray", "Ancient Saber", "Elysion", "Twin Psychogun", "Guilty Light", "Red Scorpio", "DBs Saber", "DF Field", "Morning Prayer", "S-Parts ver1.16", "Standstill Shield", "Kasami Bracer", "Secure Feet", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
"Monday": ["Kaladbolg", "Flowen's Sword", "Last Survivor", "Dragon Slayer", "Rianov 303SNR", "H&S25 Justice", "L&K14 Combat", "Crush Bullet", "Meteor Smash", "Final Impact", "Club of Zumiuran", "Brave Hammer", "Alive Aqhu", "Ice Staff: Dagon", "Double Saber", "Elysion", "Red Saber", "Meteor Cudgel", "Red Sword", "Panzer Faust", "Plantain Leaf", "Fatsia", "Sange", "Kamui", "Talis", "DBs Saber", "Guardianna", "Regenerate Gear", "DBs Shield", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
"Tuesday": ["Blade Dance", "Bloody Art", "Cross Scar", "Brionac", "Diska of Braveman", "M&A60 Vise", "Club of Laconium", "Mace of Adaman", "Twin Brand", "Brave Knuckle", "Angry Fist", "God Hand", "Red Dagger", "Maser Beam", "Asuka", "Talis", "DBs Saber", "Red Coat", "Secret Gear", "Regenerate Gear", "Black Ring", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
"Wednesday": ["Bloody Art", "Brionac", "Vjaya", "Rianov 303SNR", "Battle Verge", "Brave Hammer", "Alive Aqhu", "Soul Banish", "Red Partisan", "Yasminkov 2000H", "Yasminkov 7000V", "Maser Beam", "Musashi", "Yamato", "Zanba", "Ruby Bullet", "Sacred Guard", "S-Parts ver1.16", "S-Parts ver2.01", "Light Relief", "Attribute Wall", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
"Thursday": ["Gae Bolg", "Slicer of Assassin", "Diska of Liberator", "Diska of Braveman", "Varista", "M&A60 Vise", "Mace of Adaman", "Battle Verge", "Fire Scepter: Agni", "Ice Staff: Dagon", "Storm Wand: Indra", "Twin Brand", "Spread Needle", "Holy Ray", "Inferno Bazooka", "Victor Axe", "Flight Cutter", "Red Slicer", "Branch of Pakupaku", "Heart of Poumn", "Photon Launcher", "Guilty Light", "Talis", "Demolition Comet", "Ruby Bullet", "Guard Wave", "DF Field", "Luminous Field", "Morning Prayer", "Red Coat", "Infantry Mantle", "Regenerate Gear", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
"Friday": ["Varista", "Custom Ray ver.OO", "Bravace", "Visk-235W", "Rianov 303SNR", "M&A60 Vise", "H&S25 Justice", "Crush Bullet", "Club of Laconium", "Fire Scepter: Agni", "Victor Axe", "Caduceus", "Sting Tip", "Ancient Saber", "Red Saber", "Red Handgun", "Twin Psychogun", "Fatsia", "The Sigh of a God", "Guilty Light", "Talis", "Mahu", "Graviton Plate", "Attribute Plate", "Aura Field", "Electro Frame", "Sacred Cloth", "Smoking Plate", "Red Coat", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
"Saturday": ["Kaladbolg", "Varista", "Visk-235W", "Wals-MK2", "Justy-23ST", "Rianov 303SNR", "Club of Zumiuran", "Storm Wand: Indra", "Double Saber", "Caduceus", "Sting Tip", "Suppressed Gun", "Ancient Saber", "Twin Psychogun", "Red Mechgun", "Windmill", "Plantain Leaf", "Fatsia", "Revival Garment", "Spirit Garment", "Stink Frame", "D-Parts ver1.01", "D-Parts ver2.10", "Sense Plate", "Graviton Plate", "Custom Frame ver.OO", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
}, {
"MesetaCost": 100000,
"BaseProbability": 0x0A3D70A3, // 4%
"ProbabilityUpgrade": 0x0A3D70A3, // 4%
"Sunday": ["Zero Divide", "Asteron Belt", "Raikiri", "Skyly Card", "Purplenum Card", "Oran Card", "Guren", "Black Odoshi Red Nimaidou", "V101"],
"Monday": ["Earth Wand: Brownie", "Viridia Card", "Greenill Card", "Yellowboze Card", "Yunchang", "Black Odoshi Domaru", "Revival Cuirass", "Gratia", "Regenerate Gear B.P.", "Honeycomb Reflector", "V501", "Heavenly/Battle"],
"Tuesday": ["Zero Divide", "Asteron Belt", "Phoenix Claw", "Skyly Card", "Pinkal Card", "Whitill Card", "Morning Glory", "Ignition Cloak", "Bunny Ears", "Cat Ears", "V502", "Smartlink"],
"Wednesday": ["Phoenix Claw", "Bluefull Card", "Purplenum Card", "Pinkal Card", "Morning Glory", "Cannon Rouge", "Clio", "Morning Prayer", "Sacred Guard", "Honeycomb Reflector", "Heavenly/Legs"],
"Thursday": ["Asteron Belt", "Earth Wand: Brownie", "Phoenix Claw", "Raikiri", "Greenill Card", "Redria Card", "Whitill Card", "Flamberge", "Cannon Rouge", "Glide Divine", "Star Cuirass", "Stink Shield"],
"Friday": ["Zero Divide", "Phoenix Claw", "Raikiri", "Power Maser", "Viridia Card", "Yellowboze Card", "Ophelie Seize", "Black Odoshi Domaru", "Black Odoshi Red Nimaidou"],
"Saturday": ["Earth Wand: Brownie", "Bluefull Card", "Redria Card", "Oran Card", "Kusanagi", "Honeycomb Reflector"],
},
],
"QuestF960FailureResultItems": { // Items given when all tiers failed to give a prize
"Sunday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
"Monday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
"Tuesday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
"Wednesday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
"Thursday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
"Friday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
"Saturday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
},
// EXP multiplier for BB games. This must be an integer due to a client // EXP multiplier for BB games. This must be an integer due to a client
// limitation, and must be at least 1. // limitation, and must be at least 1.
"BBGlobalEXPMultiplier": 1, "BBGlobalEXPMultiplier": 1,
+46 -1
View File
@@ -190,11 +190,56 @@
], ],
], ],
"QuestF95FResultItems": [ "QuestF95FResultItems": [
[0, ""], // Unused [0, "000100"], // Unused
[10, "00D500"], [10, "00D500"],
[15, "000A07"], [15, "000A07"],
[20, "010157"], [20, "010157"],
], ],
"QuestF960SuccessResultItems": [
{
"MesetaCost": 1000,
"BaseProbability": 0x0A3D70A3, // 4%
"ProbabilityUpgrade": 0x0A3D70A3, // 4%
"Sunday": ["God/Power", "Cure/Poison", "Cure/Paralysis", "Cure/Slow", "Cure/Confuse", "Cure/Freeze", "Cure/Shock", "Tablet"],
"Monday": ["Three Seals", "God/Mind", "God/Arm", "Hero/Ability", "HP/Revival", "PB/Create", "Devil/Battle", "Cure/Slow"],
"Tuesday": ["God/HP", "God/Body", "PB/Create", "Cure/Poison", "Cure/Paralysis", "Cure/Freeze"],
"Wednesday": ["God/Legs", "Hero/Ability", "TP/Revival", "Devil/Battle", "Cure/Slow", "Tablet"],
"Thursday": ["God/TP", "Hero/Ability", "HP/Revival", "God/Technique", "Cure/Shock"],
"Friday": ["God/Luck", "TP/Revival", "PB/Create", "Devil/Battle", "Cure/Paralysis", "Cure/Slow", "Cure/Shock", "Tablet"],
"Saturday": ["Three Seals", "Hero/Ability", "God/Ability", "HP/Revival", "PB/Create", "Cure/Poison", "Cure/Paralysis", "Cure/Freeze"],
}, {
"MesetaCost": 10000,
"BaseProbability": 0x0A3D70A3, // 4%
"ProbabilityUpgrade": 0x0A3D70A3, // 4%
"Sunday": ["Kaladbolg", "Durandal", "Blade Dance", "M&A60 Vise", "H&S25 Justice", "L&K14 Combat", "Club of Laconium", "Photon Claw", "Silence Claw", "Stag Cutlery", "Holy Ray", "Ancient Saber", "Elysion", "Twin Psychogun", "Guilty Light", "Red Scorpio", "DBs Saber", "DF Field", "Morning Prayer", "S-Parts ver1.16", "Standstill Shield", "Kasami Bracer", "Secure Feet", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
"Monday": ["Kaladbolg", "Flowen's Sword", "Last Survivor", "Dragon Slayer", "Rianov 303SNR", "H&S25 Justice", "L&K14 Combat", "Crush Bullet", "Meteor Smash", "Final Impact", "Club of Zumiuran", "Brave Hammer", "Alive Aqhu", "Ice Staff: Dagon", "Double Saber", "Elysion", "Red Saber", "Meteor Cudgel", "Red Sword", "Panzer Faust", "Plantain Leaf", "Fatsia", "Sange", "Kamui", "Talis", "DBs Saber", "Guardianna", "Regenerate Gear", "DBs Shield", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
"Tuesday": ["Blade Dance", "Bloody Art", "Cross Scar", "Brionac", "Diska of Braveman", "M&A60 Vise", "Club of Laconium", "Mace of Adaman", "Twin Brand", "Brave Knuckle", "Angry Fist", "God Hand", "Red Dagger", "Maser Beam", "Asuka", "Talis", "DBs Saber", "Red Coat", "Secret Gear", "Regenerate Gear", "Black Ring", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
"Wednesday": ["Bloody Art", "Brionac", "Vjaya", "Rianov 303SNR", "Battle Verge", "Brave Hammer", "Alive Aqhu", "Soul Banish", "Red Partisan", "Yasminkov 2000H", "Yasminkov 7000V", "Maser Beam", "Musashi", "Yamato", "Zanba", "Ruby Bullet", "Sacred Guard", "S-Parts ver1.16", "S-Parts ver2.01", "Light Relief", "Attribute Wall", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
"Thursday": ["Gae Bolg", "Slicer of Assassin", "Diska of Liberator", "Diska of Braveman", "Varista", "M&A60 Vise", "Mace of Adaman", "Battle Verge", "Fire Scepter: Agni", "Ice Staff: Dagon", "Storm Wand: Indra", "Twin Brand", "Spread Needle", "Holy Ray", "Inferno Bazooka", "Victor Axe", "Flight Cutter", "Red Slicer", "Branch of Pakupaku", "Heart of Poumn", "Photon Launcher", "Guilty Light", "Talis", "Demolition Comet", "Ruby Bullet", "Guard Wave", "DF Field", "Luminous Field", "Morning Prayer", "Red Coat", "Infantry Mantle", "Regenerate Gear", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
"Friday": ["Varista", "Custom Ray ver.OO", "Bravace", "Visk-235W", "Rianov 303SNR", "M&A60 Vise", "H&S25 Justice", "Crush Bullet", "Club of Laconium", "Fire Scepter: Agni", "Victor Axe", "Caduceus", "Sting Tip", "Ancient Saber", "Red Saber", "Red Handgun", "Twin Psychogun", "Fatsia", "The Sigh of a God", "Guilty Light", "Talis", "Mahu", "Graviton Plate", "Attribute Plate", "Aura Field", "Electro Frame", "Sacred Cloth", "Smoking Plate", "Red Coat", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
"Saturday": ["Kaladbolg", "Varista", "Visk-235W", "Wals-MK2", "Justy-23ST", "Rianov 303SNR", "Club of Zumiuran", "Storm Wand: Indra", "Double Saber", "Caduceus", "Sting Tip", "Suppressed Gun", "Ancient Saber", "Twin Psychogun", "Red Mechgun", "Windmill", "Plantain Leaf", "Fatsia", "Revival Garment", "Spirit Garment", "Stink Frame", "D-Parts ver1.01", "D-Parts ver2.10", "Sense Plate", "Graviton Plate", "Custom Frame ver.OO", "AddSlot", "Photon Crystal", "Dragon Scale", "Rappys Beak"],
}, {
"MesetaCost": 100000,
"BaseProbability": 0x0A3D70A3, // 4%
"ProbabilityUpgrade": 0x0A3D70A3, // 4%
"Sunday": ["Zero Divide", "Asteron Belt", "Raikiri", "Skyly Card", "Purplenum Card", "Oran Card", "Guren", "Black Odoshi Red Nimaidou", "V101"],
"Monday": ["Earth Wand: Brownie", "Viridia Card", "Greenill Card", "Yellowboze Card", "Yunchang", "Black Odoshi Domaru", "Revival Cuirass", "Gratia", "Regenerate Gear B.P.", "Honeycomb Reflector", "V501", "Heavenly/Battle"],
"Tuesday": ["Zero Divide", "Asteron Belt", "Phoenix Claw", "Skyly Card", "Pinkal Card", "Whitill Card", "Morning Glory", "Ignition Cloak", "Bunny Ears", "Cat Ears", "V502", "Smartlink"],
"Wednesday": ["Phoenix Claw", "Bluefull Card", "Purplenum Card", "Pinkal Card", "Morning Glory", "Cannon Rouge", "Clio", "Morning Prayer", "Sacred Guard", "Honeycomb Reflector", "Heavenly/Legs"],
"Thursday": ["Asteron Belt", "Earth Wand: Brownie", "Phoenix Claw", "Raikiri", "Greenill Card", "Redria Card", "Whitill Card", "Flamberge", "Cannon Rouge", "Glide Divine", "Star Cuirass", "Stink Shield"],
"Friday": ["Zero Divide", "Phoenix Claw", "Raikiri", "Power Maser", "Viridia Card", "Yellowboze Card", "Ophelie Seize", "Black Odoshi Domaru", "Black Odoshi Red Nimaidou"],
"Saturday": ["Earth Wand: Brownie", "Bluefull Card", "Redria Card", "Oran Card", "Kusanagi", "Honeycomb Reflector"],
},
],
"QuestF960FailureResultItems": {
"Sunday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
"Monday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
"Tuesday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
"Wednesday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
"Thursday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
"Friday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
"Saturday": ["Monomate x1", "Dimate x1", "Trimate x1", "Monofluid x1", "Difluid x1", "Trifluid x1", "Sol Atomizer x1", "Moon Atomizer x1", "Antidote x1", "Antiparalysis x1", "Telepipe x1", "Trap Vision x1"],
},
"SecretLotteryResultItems": [ "SecretLotteryResultItems": [
"000106", "000107", "000206", "000407", "000606", "000807", "000D01", "000106", "000107", "000206", "000407", "000606", "000807", "000D01",
"001300", "002000", "002700", "002C00", "003400", "003900", "003C00", "001300", "002000", "002700", "002C00", "003400", "003900", "003C00",