diff --git a/src/ImageEncoder.cc b/src/ImageEncoder.cc index 37f93b8f..fe21e9b8 100644 --- a/src/ImageEncoder.cc +++ b/src/ImageEncoder.cc @@ -35,7 +35,7 @@ struct GVRHeader { be_uint16_t height; } __packed_ws__(GVRHeader, 0x10); -string encode_gvm(const phosg::ImageRGBA8888& img, GVRDataFormat data_format, const string& internal_name, uint32_t global_index) { +string encode_gvm(const phosg::ImageRGBA8888N& img, GVRDataFormat data_format, const string& internal_name, uint32_t global_index) { int8_t dimensions_field = -2; { size_t h = img.get_height(); diff --git a/src/ImageEncoder.hh b/src/ImageEncoder.hh index 1f6baaac..23b2347e 100644 --- a/src/ImageEncoder.hh +++ b/src/ImageEncoder.hh @@ -20,7 +20,7 @@ enum class GVRDataFormat : uint8_t { }; std::string encode_gvm( - const phosg::ImageRGBA8888& img, GVRDataFormat data_format, const std::string& internal_name, uint32_t global_index); + const phosg::ImageRGBA8888N& img, GVRDataFormat data_format, const std::string& internal_name, uint32_t global_index); phosg::ImageRGB888 decode_fon(const std::string& data, size_t width); std::string encode_fon(const phosg::ImageRGB888& img); diff --git a/src/ItemData.hh b/src/ItemData.hh index a4a8840f..e9412e13 100644 --- a/src/ItemData.hh +++ b/src/ItemData.hh @@ -118,7 +118,7 @@ struct ItemData { // sending where needed. // Related note: PSO V2 has an annoyingly complicated format for mags that // doesn't match the above table. We decode this upon receipt and encode it - // imemdiately before sending when interacting with V2 clients; see the + // immediately before sending when interacting with V2 clients; see the // implementation of decode_for_version() for details. union { diff --git a/src/Main.cc b/src/Main.cc index e4b6513a..7e152911 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -1183,7 +1183,7 @@ Action a_encode_gvm( } else { data = phosg::read_all(stdin); } - auto img = phosg::ImageRGBA8888::from_file_data(data); + auto img = phosg::ImageRGBA8888N::from_file_data(data); // If the image has any transparent pixels at all, use RGB5A3 string encoded = encode_gvm( img, has_any_transparent_pixels(img) ? GVRDataFormat::RGB5A3 : GVRDataFormat::RGB565, "image.gvr", 0); @@ -2595,17 +2595,17 @@ Action a_generate_ep3_cards_html( phosg::parallel_range([&](uint32_t index, size_t) -> bool { auto& info = this->card_infos[index]; if (!info.large_filename.empty()) { - auto img = phosg::ImageRGBA8888::from_file_data(phosg::load_file(info.large_filename)); + auto img = phosg::ImageRGBA8888N::from_file_data(phosg::load_file(info.large_filename)); img.resize(512, 399); info.large_data_url = img.serialize(phosg::ImageFormat::PNG_DATA_URL); } if (!info.medium_filename.empty()) { - auto img = phosg::ImageRGBA8888::from_file_data(phosg::load_file(info.medium_filename)); + auto img = phosg::ImageRGBA8888N::from_file_data(phosg::load_file(info.medium_filename)); img.resize(184, 144); info.medium_data_url = img.serialize(phosg::ImageFormat::PNG_DATA_URL); } if (!info.small_filename.empty()) { - auto img = phosg::ImageRGBA8888::from_file_data(phosg::load_file(info.small_filename)); + auto img = phosg::ImageRGBA8888N::from_file_data(phosg::load_file(info.small_filename)); img.resize(58, 43); info.small_data_url = img.serialize(phosg::ImageFormat::PNG_DATA_URL); } diff --git a/src/QuestScript.cc b/src/QuestScript.cc index eeea678c..ddeb03d1 100644 --- a/src/QuestScript.cc +++ b/src/QuestScript.cc @@ -830,8 +830,8 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[0] = floor // regsA[1] = section // regsA[2] = group - {0x8A, "unhide_obj", nullptr, {{REG_SET_FIXED, 3}}, F_V0_V4}, - {0x8B, "unhide_ene", nullptr, {{REG_SET_FIXED, 3}}, F_V0_V4}, + {0x8A, "construct_delayed_object", "unhide_obj", {{REG_SET_FIXED, 3}}, F_V0_V4}, + {0x8B, "construct_delayed_enemy", "unhide_ene", {{REG_SET_FIXED, 3}}, F_V0_V4}, // Starts a new thread when the player is close enough to the given point. // The collision is created on the current floor; the thread is created diff --git a/src/ServerState.cc b/src/ServerState.cc index b4e249aa..0b4d85f2 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -400,23 +400,20 @@ shared_ptr ServerState::quest_index(Version version) const { } size_t ServerState::default_min_level_for_game(Version version, Episode episode, uint8_t difficulty) const { - // A player's actual level is their displayed level - 1, so the minimums for - // Episode 1 (for example) are actually 1, 20, 40, 80. + const auto& min_levels = is_v4(version) + ? this->min_levels_v4 + : is_v3(version) + ? this->min_levels_v3 + : this->min_levels_v1_v2; switch (episode) { - case Episode::EP1: { - const auto& min_levels = (version == Version::BB_V4) ? this->min_levels_v4[0] : DEFAULT_MIN_LEVELS_V3; - return min_levels.at(difficulty); - } - case Episode::EP2: { - const auto& min_levels = (version == Version::BB_V4) ? this->min_levels_v4[1] : DEFAULT_MIN_LEVELS_V3; - return min_levels.at(difficulty); - } + case Episode::EP1: + return min_levels[0].at(difficulty); + case Episode::EP2: + return min_levels[1].at(difficulty); case Episode::EP3: return 0; - case Episode::EP4: { - const auto& min_levels = (version == Version::BB_V4) ? this->min_levels_v4[2] : DEFAULT_MIN_LEVELS_V3; - return min_levels.at(difficulty); - } + case Episode::EP4: + return min_levels[2].at(difficulty); default: throw runtime_error("invalid episode"); } @@ -966,7 +963,7 @@ void ServerState::load_config_early() { } else if (lower_path.ends_with(".gvm")) { decompressed_gvm_data = phosg::load_file(path); } else if (lower_path.ends_with(".bmp")) { - auto img = phosg::ImageRGBA8888::from_file_data(phosg::load_file(path)); + auto img = phosg::ImageRGBA8888N::from_file_data(phosg::load_file(path)); decompressed_gvm_data = encode_gvm( img, has_any_transparent_pixels(img) ? GVRDataFormat::RGB5A3 : GVRDataFormat::RGB565, @@ -1273,31 +1270,42 @@ void ServerState::load_config_early() { this->rare_enemy_rates_challenge = MapState::DEFAULT_RARE_ENEMIES; } + this->min_levels_v1_v2[0] = DEFAULT_MIN_LEVELS_V123; + this->min_levels_v1_v2[1] = DEFAULT_MIN_LEVELS_V123; + this->min_levels_v1_v2[2] = DEFAULT_MIN_LEVELS_V123; + this->min_levels_v3[0] = DEFAULT_MIN_LEVELS_V123; + this->min_levels_v3[1] = DEFAULT_MIN_LEVELS_V123; + this->min_levels_v3[2] = DEFAULT_MIN_LEVELS_V123; this->min_levels_v4[0] = DEFAULT_MIN_LEVELS_V4_EP1; this->min_levels_v4[1] = DEFAULT_MIN_LEVELS_V4_EP2; this->min_levels_v4[2] = DEFAULT_MIN_LEVELS_V4_EP4; - try { - for (const auto& ep_it : this->config_json->get_dict("BBMinimumLevels")) { - array levels({0, 0, 0, 0}); - for (size_t z = 0; z < 4; z++) { - levels[z] = ep_it.second->get_int(z) - 1; - } - switch (episode_for_token_name(ep_it.first)) { - case Episode::EP1: - this->min_levels_v4[0] = levels; - break; - case Episode::EP2: - this->min_levels_v4[1] = levels; - break; - case Episode::EP4: - this->min_levels_v4[2] = levels; - break; - default: - throw runtime_error("unknown episode"); + auto populate_min_levels = [&](std::array, 3>& dest, const char* key_name) -> void { + try { + for (const auto& ep_it : this->config_json->get_dict(key_name)) { + array levels({0, 0, 0, 0}); + for (size_t z = 0; z < 4; z++) { + levels[z] = ep_it.second->get_int(z) - 1; + } + switch (episode_for_token_name(ep_it.first)) { + case Episode::EP1: + dest[0] = levels; + break; + case Episode::EP2: + dest[1] = levels; + break; + case Episode::EP4: + dest[2] = levels; + break; + default: + throw runtime_error("unknown episode"); + } } + } catch (const out_of_range&) { } - } catch (const out_of_range&) { - } + }; + populate_min_levels(this->min_levels_v1_v2, "V1V2MinimumLevels"); + populate_min_levels(this->min_levels_v3, "V3MinimumLevels"); + populate_min_levels(this->min_levels_v4, "BBMinimumLevels"); this->bb_required_patches.clear(); try { diff --git a/src/ServerState.hh b/src/ServerState.hh index 03e87d63..29b16693 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -218,6 +218,8 @@ struct ServerState : public std::enable_shared_from_this { std::shared_ptr bb_solo_set_data_table_ep1_ult; std::array, 4> rare_enemy_rates_by_difficulty; std::shared_ptr rare_enemy_rates_challenge; + std::array, 3> min_levels_v1_v2; // Indexed as [episode][difficulty] + std::array, 3> min_levels_v3; // Indexed as [episode][difficulty] std::array, 3> min_levels_v4; // Indexed as [episode][difficulty] std::unordered_set bb_required_patches; std::unordered_set auto_patches; diff --git a/src/StaticGameData.cc b/src/StaticGameData.cc index 27e992ae..b4ec3f15 100644 --- a/src/StaticGameData.cc +++ b/src/StaticGameData.cc @@ -805,7 +805,7 @@ char char_for_challenge_rank(uint8_t rank) { return "BAS"[rank]; } -const array DEFAULT_MIN_LEVELS_V3({0, 19, 39, 79}); +const array DEFAULT_MIN_LEVELS_V123({0, 19, 39, 79}); const array DEFAULT_MIN_LEVELS_V4_EP1({0, 19, 39, 79}); const array DEFAULT_MIN_LEVELS_V4_EP2({0, 29, 49, 89}); const array DEFAULT_MIN_LEVELS_V4_EP4({0, 39, 79, 109}); diff --git a/src/StaticGameData.hh b/src/StaticGameData.hh index 63b60c8f..df622882 100644 --- a/src/StaticGameData.hh +++ b/src/StaticGameData.hh @@ -106,7 +106,7 @@ uint32_t class_flags_for_class(uint8_t char_class); char char_for_challenge_rank(uint8_t rank); -extern const std::array DEFAULT_MIN_LEVELS_V3; +extern const std::array DEFAULT_MIN_LEVELS_V123; extern const std::array DEFAULT_MIN_LEVELS_V4_EP1; extern const std::array DEFAULT_MIN_LEVELS_V4_EP2; extern const std::array DEFAULT_MIN_LEVELS_V4_EP4; diff --git a/src/TeamIndex.cc b/src/TeamIndex.cc index e88e2c22..8d42461a 100644 --- a/src/TeamIndex.cc +++ b/src/TeamIndex.cc @@ -98,7 +98,7 @@ void TeamIndex::Team::save_config() const { } void TeamIndex::Team::load_flag() { - auto img = phosg::ImageRGBA8888::from_file_data(phosg::load_file(this->flag_filename())); + auto img = phosg::ImageRGBA8888N::from_file_data(phosg::load_file(this->flag_filename())); if (img.get_width() != 32 || img.get_height() != 32) { throw runtime_error("incorrect flag image dimensions"); } @@ -114,7 +114,7 @@ void TeamIndex::Team::save_flag() const { if (!this->flag_data) { return; } - phosg::ImageRGBA8888 img(32, 32); + phosg::ImageRGBA8888N img(32, 32); for (size_t y = 0; y < 32; y++) { for (size_t x = 0; x < 32; x++) { img.write(x, y, phosg::rgba8888_for_argb1555(this->flag_data->at(y * 0x20 + x))); diff --git a/system/config.example.json b/system/config.example.json index fc6eaaa9..10bce546 100644 --- a/system/config.example.json +++ b/system/config.example.json @@ -1195,8 +1195,15 @@ // "RareEnemyRates-Ultimate": {...}, // "RareEnemyRates-Challenge": {...}, - // You can override the minimum character levels required to make BB games in + // You can override the minimum character levels required to make games in // each episode and difficulty level here. + "V1V2MinimumLevels": { + "Episode1": [1, 20, 40, 80], + }, + "V3MinimumLevels": { + "Episode1": [1, 20, 40, 80], + "Episode2": [1, 20, 40, 80], + }, "BBMinimumLevels": { "Episode1": [1, 20, 50, 90], "Episode2": [1, 30, 60, 100],