Skip to content

Commit 8caac0f

Browse files
committed
Add GameSettings to replace Events.Options
Remove OptionsMenu hacks to load settings Separate OpenVic game settings from dataloading settings Remove SoundTab script
1 parent cb74796 commit 8caac0f

18 files changed

Lines changed: 491 additions & 101 deletions

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="GameSettings" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
3+
<brief_description>
4+
</brief_description>
5+
<description>
6+
</description>
7+
<tutorials>
8+
</tutorials>
9+
<methods>
10+
<method name="get_section_keys" qualifiers="const">
11+
<return type="PackedStringArray" />
12+
<param index="0" name="section" type="String" />
13+
<description>
14+
Returns an array of all defined key identifiers in the specified section. Raises an error and returns an empty array if the section does not exist.
15+
See [method ConfigFile.get_section_keys].
16+
</description>
17+
</method>
18+
<method name="get_sections" qualifiers="const">
19+
<return type="PackedStringArray" />
20+
<description>
21+
Returns an array of all defined section identifiers.
22+
See [method ConfigFile.get_sections].
23+
</description>
24+
</method>
25+
<method name="get_value">
26+
<return type="Variant" />
27+
<param index="0" name="section" type="String" />
28+
<param index="1" name="key" type="String" />
29+
<param index="2" name="default" type="Variant" default="null" />
30+
<description>
31+
Returns the current value for the specified section and key. If either the section or the key do not exist, the method returns the fallback [param default]. If [param default] is not specified, is set to null when previously not set, or is not equal to the previously set value, an error is also raised.
32+
See [method ConfigFile.get_value].
33+
</description>
34+
</method>
35+
<method name="has_section" qualifiers="const">
36+
<return type="bool" />
37+
<param index="0" name="section" type="String" />
38+
<description>
39+
Returns [code]true[/code] if the specified section exists.
40+
See [method ConfigFile.has_section].
41+
</description>
42+
</method>
43+
<method name="has_section_key" qualifiers="const">
44+
<return type="bool" />
45+
<param index="0" name="section" type="String" />
46+
<param index="1" name="key" type="String" />
47+
<description>
48+
Returns [code]true[/code] if the specified section-key pair exists.
49+
See [method ConfigFile.has_section_key].
50+
</description>
51+
</method>
52+
<method name="load">
53+
<return type="int" enum="Error" />
54+
<param index="0" name="path" type="String" default="&quot;&quot;" />
55+
<description>
56+
Loads the settings file specified by [param path] and then emitting [signal Resource.changed]. If [param path] is empty then it is set to [member Resource.resource_path]. The file's contents are parsed and loaded in the [GameSettings] object which the method was called on. Sets [member Resource.resource_path] to [param path].
57+
Returns [constant OK] on success, or one of the other [enum Error] values if the operation failed.
58+
See [method ConfigFile.load].
59+
</description>
60+
</method>
61+
<method name="load_deprecated_file">
62+
<return type="int" enum="Error" />
63+
<param index="0" name="path" type="String" />
64+
<param index="1" name="section_keys" type="Dictionary" />
65+
<description>
66+
Loads the settings file specified by [param path] and then emitting [signal Resource.changed], loading only the contents found in [param section_keys] into the [GameSettings] object which the method was called on.
67+
Returns [constant OK] on success, or one of the other [enum Error] values if the operation failed. Returns [constant ERR_INVALID_PARAMETER] if [param section_keys] contains invalid values.
68+
69+
Each key of [param section_keys] corresponds to a section name where each value corresponds to keys that can be:
70+
- [code]null[/code]: Will copy the entire section.
71+
- [String] or [StringName]: Will copy the specified section-key pair.
72+
- [PackedStringArray] or [Array] containing [String] or [StringName] values: Will copy every contained key in the specified section.
73+
</description>
74+
</method>
75+
<method name="load_from_file" qualifiers="static">
76+
<return type="GameSettings" />
77+
<param index="0" name="path" type="String" />
78+
<description>
79+
Loads a settings file specified by [param path] if [method ResourceLoader.has_cached] returns [code]false[/code] for [param path]. If [param path] is cached then it will load the reference to the cached [GameSettings] resource instead, this will not reload the [GameSettings] config from the file, call [method load] instead.
80+
Note: This will recursively create the directories to [param path] if none exist. It will save an empty text file if the file does not exist.
81+
</description>
82+
</method>
83+
<method name="reset_section">
84+
<return type="void" />
85+
<param index="0" name="section" type="String" />
86+
<description>
87+
Resets all the key-value pairs inside the section to their initial default value, emitting [signal Resource.changed]. Raises an error if the section does not have defaults.
88+
</description>
89+
</method>
90+
<method name="reset_section_key">
91+
<return type="void" />
92+
<param index="0" name="section" type="String" />
93+
<param index="1" name="key" type="String" />
94+
<description>
95+
Resets the specified key inside the section to their initial default value, emitting [signal Resource.changed]. Raises an error if either the section-key pair does not have a default.
96+
</description>
97+
</method>
98+
<method name="reset_settings">
99+
<return type="void" />
100+
<description>
101+
Resets every key to its initial default value, emitting [signal Resource.changed].
102+
</description>
103+
</method>
104+
<method name="save">
105+
<return type="int" enum="Error" />
106+
<param index="0" name="path" type="String" default="&quot;&quot;" />
107+
<description>
108+
Saves the contents of the [GameSettings] object to the file specified by as [param path], including its default values. If [param path] is empty then it is set to [member Resource.resource_path]. The output file uses an INI-style structure.
109+
Returns [constant OK] on success, or one of the other [enum Error] values if the operation failed.
110+
See [method ConfigFile.save].
111+
</description>
112+
</method>
113+
<method name="set_value">
114+
<return type="void" />
115+
<param index="0" name="section" type="String" />
116+
<param index="1" name="key" type="String" />
117+
<param index="2" name="value" type="Variant" />
118+
<description>
119+
Assigns a value to the specified key of the specified section. If either the section or the key do not exist, they are created. Passing a [code]null[/code] value resets the specified key to its default without emitting [signal Resource.changed], raising an error if a default is not set.
120+
See [method ConfigFile.set_value].
121+
</description>
122+
</method>
123+
</methods>
124+
</class>
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
#include "GameSettings.hpp"
2+
3+
#include <godot_cpp/classes/config_file.hpp>
4+
#include <godot_cpp/classes/dir_access.hpp>
5+
#include <godot_cpp/classes/file_access.hpp>
6+
#include <godot_cpp/classes/global_constants.hpp>
7+
#include <godot_cpp/classes/resource_loader.hpp>
8+
#include <godot_cpp/core/error_macros.hpp>
9+
#include <godot_cpp/core/object.hpp>
10+
#include <godot_cpp/core/print_string.hpp>
11+
#include <godot_cpp/core/property_info.hpp>
12+
#include <godot_cpp/variant/dictionary.hpp>
13+
#include <godot_cpp/variant/packed_string_array.hpp>
14+
#include <godot_cpp/variant/variant.hpp>
15+
16+
#include "openvic-extension/utility/ClassBindings.hpp"
17+
18+
using namespace OpenVic;
19+
using namespace godot;
20+
21+
GameSettings::GameSettings() {
22+
config.instantiate();
23+
defaults.instantiate();
24+
}
25+
26+
void GameSettings::reset_settings() {
27+
config = defaults;
28+
emit_changed();
29+
}
30+
31+
Error GameSettings::save(String path) {
32+
if (path.is_empty()) {
33+
path = get_path();
34+
}
35+
for (String const& section : defaults->get_sections()) {
36+
for (String const& key : defaults->get_section_keys(section)) {
37+
if (!has_section_key(section, key)) {
38+
config->set_value(section, key, defaults->get_value(section, key));
39+
}
40+
}
41+
}
42+
Error err = config->save(path);
43+
return err;
44+
}
45+
46+
Error GameSettings::load(String path) {
47+
if (path.is_empty()) {
48+
path = get_path();
49+
} else {
50+
set_path(path);
51+
}
52+
53+
Error err = config->load(path);
54+
emit_changed();
55+
return err;
56+
}
57+
58+
Ref<GameSettings> GameSettings::load_from_file(String path) {
59+
Ref<GameSettings> settings;
60+
if (ResourceLoader::get_singleton()->has_cached(path)) {
61+
settings = ResourceLoader::get_singleton()->get_cached_ref(path);
62+
if (settings.is_valid()) {
63+
return settings;
64+
}
65+
}
66+
67+
settings.instantiate();
68+
if (!FileAccess::file_exists(path)) {
69+
if (String dir = path.get_base_dir(); !DirAccess::dir_exists_absolute(dir)) {
70+
DirAccess::make_dir_recursive_absolute(dir);
71+
}
72+
73+
ERR_FAIL_COND_V(settings->save(path) != OK, Ref<GameSettings>());
74+
} else {
75+
ERR_FAIL_COND_V(settings->load(path) != OK, Ref<GameSettings>());
76+
}
77+
return settings;
78+
}
79+
80+
void GameSettings::set_value(String section, String key, Variant value) {
81+
if (value == Variant()) {
82+
config->set_value(section, key, defaults->get_value(section, key));
83+
return;
84+
}
85+
86+
config->set_value(section, key, value);
87+
}
88+
89+
Variant GameSettings::get_value(String section, String key, Variant default_value) {
90+
if (default_value != Variant()) {
91+
if (defaults->has_section_key(section, key)) {
92+
ERR_FAIL_COND_V_MSG(
93+
defaults->get_value(section, key) != default_value, nullptr,
94+
vformat("Setting '%s.%s' default value does match previously set default value.", section, key)
95+
);
96+
} else {
97+
defaults->set_value(section, key, default_value);
98+
}
99+
} else if (defaults->has_section_key(section, key)) {
100+
default_value = defaults->get_value(section, key);
101+
}
102+
103+
if (!has_section_key(section, key)) {
104+
set_value(section, key, default_value);
105+
return default_value;
106+
}
107+
108+
return config->get_value(section, key);
109+
}
110+
111+
bool GameSettings::has_section(String section) const {
112+
return config->has_section(section);
113+
}
114+
115+
bool GameSettings::has_section_key(String section, String key) const {
116+
return config->has_section_key(section, key);
117+
}
118+
119+
void GameSettings::reset_section(String section) {
120+
set_block_signals(true);
121+
for (String const& key : config->get_section_keys(section)) {
122+
reset_section_key(section, key);
123+
}
124+
set_block_signals(false);
125+
emit_changed();
126+
}
127+
128+
void GameSettings::reset_section_key(String section, String key) {
129+
config->set_value(section, key, defaults->get_value(section, key));
130+
emit_changed();
131+
}
132+
133+
PackedStringArray GameSettings::get_sections() const {
134+
return config->get_sections();
135+
}
136+
137+
PackedStringArray GameSettings::get_section_keys(String section) const {
138+
return config->get_section_keys(section);
139+
}
140+
141+
Error GameSettings::load_deprecated_file(String path, Dictionary section_keys) {
142+
Ref<ConfigFile> config;
143+
config.instantiate();
144+
if (Error err = config->load(path); err != OK) {
145+
return err;
146+
}
147+
148+
Array sections = section_keys.keys();
149+
for (size_t i = 0; i < sections.size(); i++) {
150+
Variant section = sections[i];
151+
const Variant::Type section_type = section.get_type();
152+
ERR_FAIL_COND_V(section_type != Variant::STRING && section_type != Variant::STRING_NAME, ERR_INVALID_PARAMETER);
153+
154+
Variant key = section_keys[section];
155+
switch (key.get_type()) {
156+
using enum Variant::Type;
157+
case NIL:
158+
for (String const& key : config->get_section_keys(section)) {
159+
set_value(section, key, config->get_value(section, key));
160+
}
161+
break;
162+
case STRING:
163+
case STRING_NAME:
164+
if (config->has_section_key(section, key) && !has_section_key(section, key)) {
165+
set_value(section, key, config->get_value(section, key));
166+
}
167+
break;
168+
169+
case PACKED_STRING_ARRAY: {
170+
PackedStringArray array = key;
171+
for (String const& k : array) {
172+
if (config->has_section_key(section, k) && !has_section_key(section, k)) {
173+
set_value(section, k, config->get_value(section, k));
174+
}
175+
}
176+
break;
177+
}
178+
179+
case ARRAY: {
180+
Array array = key;
181+
for (size_t arr_i = 0; arr_i < array.size(); arr_i++) {
182+
Variant const& inner_key = array[arr_i];
183+
Variant::Type inner_key_type = inner_key.get_type();
184+
ERR_FAIL_COND_V(
185+
inner_key_type != Variant::STRING && inner_key_type != Variant::STRING_NAME, ERR_INVALID_PARAMETER
186+
);
187+
188+
String const& k = inner_key;
189+
if (config->has_section_key(section, k) && !has_section_key(section, k)) {
190+
set_value(section, k, config->get_value(section, k));
191+
}
192+
}
193+
break;
194+
}
195+
196+
default: //
197+
ERR_FAIL_V(ERR_INVALID_PARAMETER);
198+
}
199+
}
200+
emit_changed();
201+
202+
return OK;
203+
}
204+
205+
void GameSettings::_bind_methods() {
206+
OV_BIND_METHOD(GameSettings::reset_settings);
207+
208+
OV_BIND_METHOD(GameSettings::save, { "path" }, DEFVAL(String()));
209+
OV_BIND_METHOD(GameSettings::load, { "path" }, DEFVAL(String()));
210+
211+
OV_BIND_SMETHOD(GameSettings::load_from_file, { "path" });
212+
213+
OV_BIND_METHOD(GameSettings::set_value, { "section", "key", "value" });
214+
OV_BIND_METHOD(GameSettings::get_value, { "section", "key", "default" }, DEFVAL(nullptr));
215+
216+
OV_BIND_METHOD(GameSettings::has_section, { "section" });
217+
OV_BIND_METHOD(GameSettings::has_section_key, { "section", "key" });
218+
219+
OV_BIND_METHOD(GameSettings::reset_section, { "section" });
220+
OV_BIND_METHOD(GameSettings::reset_section_key, { "section", "key" });
221+
222+
OV_BIND_METHOD(GameSettings::get_sections);
223+
OV_BIND_METHOD(GameSettings::get_section_keys, { "section" });
224+
225+
OV_BIND_METHOD(GameSettings::load_deprecated_file, { "path", "section_keys" });
226+
}

0 commit comments

Comments
 (0)