diff --git a/extension/doc_classes/GameSingleton.xml b/extension/doc_classes/GameSingleton.xml index ef492e01..bd00d187 100644 --- a/extension/doc_classes/GameSingleton.xml +++ b/extension/doc_classes/GameSingleton.xml @@ -89,6 +89,15 @@ Returns the localization key [String] of the mapmode with the specified [param index]. + + + + Returns an [Array] of [Dictionary] containing information about available mods, with each [Dictionary] containing values for the following [StringName] keys: + - [code]"identifier"[/code] - The mod's [String] name + - [code]"dependencies"[/code] - A [PackedStringArray] of dependency mod names + - [code]"is_loaded"[/code] - Is [code]true[/code] if the mod is a currently loaded mod, else [code]false[/code] + + @@ -153,8 +162,10 @@ + - Load compatibility mode text defines, localization string and map and flag images. Returns [code]FAILED[/code] if there are any problems when loading all this data, otherwise returns [code]OK[/code]. + Load compatibility mode text defines, localization string and map and flag images, loading data from [param mods] if not empty. Returns [code]FAILED[/code] if there are any problems when loading all this data, otherwise returns [code]OK[/code]. + See [method get_mod_info] for a valid list of values. @@ -173,10 +184,9 @@ - - + - Set the dataloading roots to those provided in [param file_paths], ignoring the filepaths in [param replace_paths] in favor of mods, which should contain full filepaths to the base game's installation and to any mods that are to be loaded on top of it. Returns [code]FAILED[/code] if there are any problems when setting the dataloading roots, otherwise returns [code]OK[/code]. + Set the dataloading roots to those provided in [param path] which should contain full filepaths to the base game's installation and where the search for mods will begin from. Returns [code]FAILED[/code] if there are any problems when setting the dataloading roots, otherwise returns [code]OK[/code]. diff --git a/extension/src/openvic-extension/singletons/GameSingleton.cpp b/extension/src/openvic-extension/singletons/GameSingleton.cpp index f24c4c67..42df09a0 100644 --- a/extension/src/openvic-extension/singletons/GameSingleton.cpp +++ b/extension/src/openvic-extension/singletons/GameSingleton.cpp @@ -4,8 +4,10 @@ #include #include +#include #include +#include #include #include @@ -16,6 +18,8 @@ #include "openvic-extension/core/Bind.hpp" #include "openvic-extension/utility/Utilities.hpp" +#include + #include #include @@ -39,12 +43,14 @@ StringName const& GameSingleton::_signal_mapmode_changed() { void GameSingleton::_bind_methods() { OV_BIND_SMETHOD(setup_logger); - OV_BIND_METHOD(GameSingleton::load_defines_compatibility_mode); - OV_BIND_METHOD(GameSingleton::set_compatibility_mode_roots, { "file_paths", "replace_paths" }, DEFVAL(PackedStringArray{})); + OV_BIND_METHOD(GameSingleton::load_defines_compatibility_mode, { "mods" }, DEFVAL(PackedStringArray())); + OV_BIND_METHOD(GameSingleton::set_compatibility_mode_roots, { "path" }); OV_BIND_SMETHOD(search_for_game_path, { "hint_path" }, DEFVAL(String {})); OV_BIND_METHOD(GameSingleton::lookup_file_path, { "path" }); + OV_BIND_METHOD(GameSingleton::get_mod_info); + OV_BIND_METHOD(GameSingleton::get_bookmark_info); OV_BIND_METHOD(GameSingleton::setup_game, { "bookmark_index" }); OV_BIND_METHOD(GameSingleton::is_game_instance_setup); @@ -143,6 +149,37 @@ void GameSingleton::setup_logger() { spdlog::default_logger_raw()->sinks().push_back(std::move(godot_sink)); } +TypedArray GameSingleton::get_mod_info() const { + static const StringName identifier_key = "identifier"; + static const StringName dependencies_key = "dependencies"; + static const StringName is_loaded_key = "is_loaded"; + + TypedArray results; + + ModManager const& mod_manager = game_manager.get_mod_manager(); + memory::vector const& loaded_mods = mod_manager.get_loaded_mods(); + + for (Mod const& mod : mod_manager.get_mods()) { + Dictionary mod_info_dictionary; + + mod_info_dictionary[identifier_key] = Utilities::std_to_godot_string(mod.get_identifier()); + + mod_info_dictionary[dependencies_key] = [&]() -> PackedStringArray { + PackedStringArray result; + for (std::string_view dep_id : mod.get_dependencies()) { + result.push_back(Utilities::std_to_godot_string(dep_id)); + } + return result; + }(); + + mod_info_dictionary[is_loaded_key] = ranges::contains(loaded_mods, &mod); + + results.push_back(std::move(mod_info_dictionary)); + } + + return results; +} + TypedArray GameSingleton::get_bookmark_info() const { static const StringName bookmark_info_name_key = "bookmark_name"; static const StringName bookmark_info_date_key = "bookmark_date"; @@ -662,30 +699,26 @@ Error GameSingleton::_load_flag_sheet() { return ret; } -Error GameSingleton::set_compatibility_mode_roots( - PackedStringArray const& file_paths, godot::PackedStringArray const& replace_paths -) { - Dataloader::path_vector_t roots; - roots.reserve(file_paths.size()); - for (String const& path : file_paths) { - roots.emplace_back(Utilities::godot_to_std_string(path)); - } - - // TODO @BrickPI https://github.com/OpenVicProject/OpenVic/pull/512 - // Dataloader::path_vector_t replace; - // replace.reserve(replace_paths.size()); - // for (String const& path : replace_paths) { - // replace.emplace_back(Utilities::godot_to_std_string(path)); - // } - +Error GameSingleton::set_compatibility_mode_roots(String const& path) { + Dataloader::path_vector_t roots { Utilities::godot_to_std_string(path) }; ERR_FAIL_COND_V_MSG(!game_manager.set_base_path(roots), FAILED, "Failed to set dataloader roots!"); return OK; } -Error GameSingleton::load_defines_compatibility_mode() { +Error GameSingleton::load_defines_compatibility_mode(PackedStringArray const& mods) { Error err = OK; auto add_message = std::bind_front(&LoadLocalisation::add_message, LoadLocalisation::get_singleton()); + ERR_FAIL_COND_V_MSG(!game_manager.load_mod_descriptors(), FAILED, "Failed to load mod descriptors!"); + + memory::vector std_mods; + std_mods.reserve(mods.size()); + for (String const& mod : mods) { + std_mods.emplace_back(Utilities::godot_to_std_string(mod)); + } + + ERR_FAIL_COND_V_MSG(!game_manager.load_mods(std_mods), FAILED, "Loading mods failed."); + if (!game_manager.load_definitions(add_message)) { UtilityFunctions::push_error("Failed to load defines!"); err = FAILED; diff --git a/extension/src/openvic-extension/singletons/GameSingleton.hpp b/extension/src/openvic-extension/singletons/GameSingleton.hpp index 28b18871..167124b9 100644 --- a/extension/src/openvic-extension/singletons/GameSingleton.hpp +++ b/extension/src/openvic-extension/singletons/GameSingleton.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -66,14 +67,14 @@ namespace OpenVic { /* Load the game's defines in compatibility mode from the filepath * pointing to the defines folder. */ - godot::Error set_compatibility_mode_roots( - godot::PackedStringArray const& file_paths, godot::PackedStringArray const& replace_paths = {} - ); - godot::Error load_defines_compatibility_mode(); + godot::Error set_compatibility_mode_roots(godot::String const& path); + godot::Error load_defines_compatibility_mode(godot::PackedStringArray const& mods = {}); static godot::String search_for_game_path(godot::String const& hint_path = {}); godot::String lookup_file_path(godot::String const& path) const; + godot::TypedArray get_mod_info() const; + godot::TypedArray get_bookmark_info() const; /* After initial load or resigning a previous session game setup, all mutable components of the simulation diff --git a/game/src/Systems/Startup/GameStart.gd b/game/src/Systems/Startup/GameStart.gd index 329d697d..d9e408e8 100644 --- a/game/src/Systems/Startup/GameStart.gd +++ b/game/src/Systems/Startup/GameStart.gd @@ -13,7 +13,7 @@ const GameMenuScene := preload("res://src/UI/GameMenu/GameMenu/GameMenu.tscn") @export var setting_name : String = "base_defines_path" var _settings_base_path : String = "" -var _compatibility_path_list : PackedStringArray = [] +var _mod_list : PackedStringArray = [] var _vic2_settings : GameSettings func _enter_tree() -> void: @@ -115,21 +115,22 @@ func _setup_compatibility_mode_paths() -> void: _vic2_settings.set_value(section_name, setting_name, actual_base_path) _vic2_settings.emit_changed() - _compatibility_path_list = [actual_base_path] - # Add mod paths - var settings_mod_names : PackedStringArray = ArgumentParser.get_option_value(&"mod") - for mod_name : String in settings_mod_names: - _compatibility_path_list.push_back(actual_base_path + "/mod/" + mod_name) + var mod_status_file := ConfigFile.new() + mod_status_file.load("user://mods.cfg") + _mod_list = mod_status_file.get_value("mods", "load_list", []) + for mod in ArgumentParser.get_option_value(&"mod"): + if mod not in _mod_list and mod != "": + _mod_list.push_back(mod) func _load_compatibility_mode() -> void: - if GameSingleton.set_compatibility_mode_roots(_compatibility_path_list) != OK: + if GameSingleton.set_compatibility_mode_roots(_settings_base_path) != OK: push_error("Errors setting game roots!") CursorManager.initial_cursor_setup() setup_title_theme() - if GameSingleton.load_defines_compatibility_mode() != OK: + if GameSingleton.load_defines_compatibility_mode(_mod_list) != OK: push_error("Errors loading game defines!") SoundSingleton.load_sounds()