Add settings that persist
This commit is contained in:
parent
c1a534d2c2
commit
790874d4ca
77
Cargo.lock
generated
77
Cargo.lock
generated
@ -41,6 +41,12 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "gdextension-api"
|
||||
version = "0.5.1"
|
||||
@ -138,12 +144,28 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.186"
|
||||
@ -181,6 +203,7 @@ dependencies = [
|
||||
"godot",
|
||||
"serde",
|
||||
"teanet",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -222,6 +245,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.119"
|
||||
@ -262,6 +294,45 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
@ -278,6 +349,12 @@ dependencies = [
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.54"
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
[gd_scene format=3 uid="uid://lahirbqfppvw"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bbmc2gmy7g8ag" path="res://scenes/misc/menu_player.tscn" id="1_o5qli"]
|
||||
[ext_resource type="PackedScene" uid="uid://n3u0jr4wiib1" path="res://scenes/ui/settings.tscn" id="2_0wfyh"]
|
||||
|
||||
[node name="menu" type="MainMenu" unique_id=2036763876 node_paths=PackedStringArray("camera", "main_position", "host_position", "main_panel", "host_panel", "join_panel", "host_port", "join_addr", "popup_panel", "popup_title", "popup_text", "popup_button")]
|
||||
camera = NodePath("camera_3d")
|
||||
@ -175,6 +176,9 @@ text = "Join"
|
||||
layout_mode = 2
|
||||
text = "Back"
|
||||
|
||||
[node name="settings" parent="." unique_id=999912383 instance=ExtResource("2_0wfyh")]
|
||||
visible = false
|
||||
|
||||
[node name="main_pos" type="Node3D" parent="." unique_id=98241074]
|
||||
transform = Transform3D(0.98629165, -0.031186387, 0.16203767, -1.4054484e-08, 0.981978, 0.1889952, -0.16501155, -0.18640435, 0.9685167, 1.0974054, 1.7166122, 1.4909104)
|
||||
|
||||
@ -232,6 +236,7 @@ text = "Ok"
|
||||
|
||||
[connection signal="pressed" from="main_panel/v_box_container/host" to="." method="go_to_host_game"]
|
||||
[connection signal="pressed" from="main_panel/v_box_container/join" to="." method="go_to_join_game"]
|
||||
[connection signal="pressed" from="main_panel/v_box_container/settings" to="settings" method="open"]
|
||||
[connection signal="pressed" from="main_panel/v_box_container/quit" to="." method="quit_game"]
|
||||
[connection signal="pressed" from="host_panel/v_box_container/host" to="." method="host_game"]
|
||||
[connection signal="pressed" from="host_panel/v_box_container/back" to="." method="go_to_main_menu"]
|
||||
|
||||
54
godot/scenes/ui/settings.tscn
Normal file
54
godot/scenes/ui/settings.tscn
Normal file
@ -0,0 +1,54 @@
|
||||
[gd_scene format=3 uid="uid://n3u0jr4wiib1"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://cqiwdajl71ujv" path="res://scenes/ui/slider.tscn" id="1_8wfqe"]
|
||||
|
||||
[node name="settings" type="SettingsPanel" unique_id=999912383 node_paths=PackedStringArray("settings_grid")]
|
||||
settings_grid = NodePath("grid")
|
||||
slider_scene = ExtResource("1_8wfqe")
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -207.0
|
||||
offset_top = -112.0
|
||||
offset_right = 207.0
|
||||
offset_bottom = 112.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="label" type="Label" parent="." unique_id=2036746921]
|
||||
layout_mode = 0
|
||||
offset_left = 57.0
|
||||
offset_right = 357.0
|
||||
offset_bottom = 23.0
|
||||
text = "Settings"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="grid" type="GridContainer" parent="." unique_id=833231169]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
custom_maximum_size = Vector2(400, -1)
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -145.0
|
||||
offset_top = -74.0
|
||||
offset_right = 155.0
|
||||
offset_bottom = 67.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
columns = 2
|
||||
|
||||
[node name="button" type="Button" parent="." unique_id=1301469030]
|
||||
layout_mode = 0
|
||||
offset_left = 182.0
|
||||
offset_top = 192.0
|
||||
offset_right = 232.0
|
||||
offset_bottom = 223.0
|
||||
size_flags_horizontal = 4
|
||||
text = "Close"
|
||||
|
||||
[connection signal="pressed" from="button" to="." method="close"]
|
||||
19
godot/scenes/ui/slider.tscn
Normal file
19
godot/scenes/ui/slider.tscn
Normal file
@ -0,0 +1,19 @@
|
||||
[gd_scene format=3 uid="uid://cqiwdajl71ujv"]
|
||||
|
||||
[node name="slider" type="SliderContainer" unique_id=2000698366 node_paths=PackedStringArray("label", "slider")]
|
||||
label = NodePath("label")
|
||||
slider = NodePath("h_slider")
|
||||
value = 50.0
|
||||
max = 100.0
|
||||
offset_right = 214.0
|
||||
offset_bottom = 23.0
|
||||
|
||||
[node name="label" type="Label" parent="." unique_id=2059967949]
|
||||
layout_mode = 2
|
||||
text = "50"
|
||||
|
||||
[node name="h_slider" type="HSlider" parent="." unique_id=1564648919]
|
||||
custom_minimum_size = Vector2(200, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
value = 50.0
|
||||
@ -10,3 +10,4 @@ crate-type = ["cdylib"] # Compile this crate to a dynamic C library.
|
||||
godot = "0.5.4"
|
||||
serde = "1.0.228"
|
||||
teanet = { path = "../teanet-rust" }
|
||||
toml = "1.1.3"
|
||||
|
||||
47
rust/src/game_settings.rs
Normal file
47
rust/src/game_settings.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use godot::{
|
||||
classes::{FileAccess, file_access::ModeFlags},
|
||||
prelude::*,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct GameSettings {
|
||||
pub fov: f64,
|
||||
}
|
||||
|
||||
impl Default for GameSettings {
|
||||
fn default() -> Self {
|
||||
Self { fov: 90. }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=Node, singleton)]
|
||||
pub struct GameSettingsManager {
|
||||
pub settings: GameSettings,
|
||||
base: Base<Node>,
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl INode for GameSettingsManager {
|
||||
fn init(base: Base<Node>) -> GameSettingsManager {
|
||||
let mut settings = GameSettings::default();
|
||||
if let Some(file) = FileAccess::open("user://config.toml", ModeFlags::READ) {
|
||||
if let Ok(deserialized) = toml::from_str(&file.get_as_text().to_string()) {
|
||||
settings = deserialized;
|
||||
}
|
||||
}
|
||||
GameSettingsManager { settings, base }
|
||||
}
|
||||
}
|
||||
|
||||
impl GameSettingsManager {
|
||||
pub fn update_settings(&mut self, settings: GameSettings) {
|
||||
self.settings = settings;
|
||||
if let Some(mut file) = FileAccess::open("user://config.toml", ModeFlags::WRITE) {
|
||||
if let Ok(serialized) = toml::to_string(&self.settings) {
|
||||
file.store_string(&serialized);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ use godot::prelude::*;
|
||||
pub mod ball;
|
||||
pub mod cli_parser;
|
||||
pub mod game_manager;
|
||||
pub mod game_settings;
|
||||
pub mod goal;
|
||||
pub mod killbox;
|
||||
pub mod map;
|
||||
|
||||
@ -6,4 +6,6 @@ pub mod main_menu;
|
||||
pub mod menu_player;
|
||||
pub mod net_stats;
|
||||
pub mod player_listing;
|
||||
pub mod settings;
|
||||
pub mod simple_score_view;
|
||||
pub mod slider;
|
||||
|
||||
137
rust/src/ui/settings.rs
Normal file
137
rust/src/ui/settings.rs
Normal file
@ -0,0 +1,137 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use godot::{
|
||||
classes::{Control, GridContainer, IPanel, Label, Panel, control::SizeFlags},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
game_settings::{GameSettings, GameSettingsManager},
|
||||
ui::slider::SliderContainer,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Hash)]
|
||||
pub enum Setting {
|
||||
FoV,
|
||||
}
|
||||
|
||||
impl ToString for Setting {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Setting::FoV => "Field of View",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum SettingValue {
|
||||
SliderNumber(SliderValue),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct SliderValue {
|
||||
min: f64,
|
||||
max: f64,
|
||||
value: f64,
|
||||
}
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=Panel, init)]
|
||||
pub struct SettingsPanel {
|
||||
#[export]
|
||||
settings_grid: Option<Gd<GridContainer>>,
|
||||
#[export]
|
||||
slider_scene: Option<Gd<PackedScene>>,
|
||||
|
||||
settings: HashMap<Setting, SettingValue>,
|
||||
|
||||
base: Base<Panel>,
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl IPanel for SettingsPanel {
|
||||
fn ready(&mut self) {
|
||||
let singleton = GameSettingsManager::singleton();
|
||||
let settings = &singleton.bind().settings;
|
||||
self.settings.insert(
|
||||
Setting::FoV,
|
||||
SettingValue::SliderNumber(SliderValue {
|
||||
min: 60.,
|
||||
max: 120.,
|
||||
value: settings.fov,
|
||||
}),
|
||||
);
|
||||
|
||||
if let Some(mut grid) = self.settings_grid.clone() {
|
||||
for (setting, value) in &self.settings {
|
||||
let mut label = Label::new_alloc();
|
||||
label.set_text(&setting.to_string());
|
||||
label.set_h_size_flags(SizeFlags::EXPAND | SizeFlags::SHRINK_BEGIN);
|
||||
grid.add_child(&label);
|
||||
|
||||
if let Some(scene) = self.slider_scene.clone() {
|
||||
let node = match value {
|
||||
SettingValue::SliderNumber(slider_value) => {
|
||||
let mut slider = scene.instantiate_as::<SliderContainer>();
|
||||
|
||||
slider.bind_mut().min = slider_value.min;
|
||||
slider.bind_mut().max = slider_value.max;
|
||||
slider.bind_mut().value = slider_value.value;
|
||||
|
||||
let setting_clone = setting.clone();
|
||||
let value_clone = slider_value.clone();
|
||||
slider.bind_mut().signals().on_change().connect_other(
|
||||
self,
|
||||
move |s, new_value| {
|
||||
s.run_deferred(move |s| {
|
||||
s.on_change(
|
||||
setting_clone,
|
||||
SettingValue::SliderNumber(SliderValue {
|
||||
value: new_value,
|
||||
..value_clone
|
||||
}),
|
||||
)
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
slider.upcast::<Control>()
|
||||
}
|
||||
};
|
||||
|
||||
grid.add_child(&node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl SettingsPanel {
|
||||
pub fn on_change(&mut self, setting: Setting, new_value: SettingValue) {
|
||||
self.settings.insert(setting, new_value);
|
||||
|
||||
let mut new_settings = GameSettingsManager::singleton().bind().settings.clone();
|
||||
for (setting, value) in &self.settings {
|
||||
match (setting, value) {
|
||||
(Setting::FoV, SettingValue::SliderNumber(value)) => {
|
||||
new_settings.fov = value.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
GameSettingsManager::singleton()
|
||||
.bind_mut()
|
||||
.update_settings(new_settings);
|
||||
}
|
||||
|
||||
#[func]
|
||||
pub fn open(&mut self) {
|
||||
self.base_mut().set_visible(true);
|
||||
}
|
||||
|
||||
#[func]
|
||||
pub fn close(&mut self) {
|
||||
self.base_mut().set_visible(false);
|
||||
}
|
||||
}
|
||||
56
rust/src/ui/slider.rs
Normal file
56
rust/src/ui/slider.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use godot::{
|
||||
classes::{HBoxContainer, IHBoxContainer, Label, Slider},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=HBoxContainer, init, tool)]
|
||||
pub struct SliderContainer {
|
||||
#[export]
|
||||
label: Option<Gd<Label>>,
|
||||
#[export]
|
||||
slider: Option<Gd<Slider>>,
|
||||
#[export]
|
||||
pub value: f64,
|
||||
#[export]
|
||||
pub min: f64,
|
||||
#[export]
|
||||
pub max: f64,
|
||||
|
||||
last_value: f64,
|
||||
|
||||
base: Base<HBoxContainer>,
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl IHBoxContainer for SliderContainer {
|
||||
fn ready(&mut self) {}
|
||||
|
||||
fn process(&mut self, _delta: f64) {
|
||||
if let Some(slider) = &mut self.slider {
|
||||
slider.set_min(self.min);
|
||||
slider.set_max(self.max);
|
||||
if self.last_value != self.value {
|
||||
slider.set_value(self.value);
|
||||
}
|
||||
self.value = slider.get_value();
|
||||
if self.last_value != self.value {
|
||||
let value = self.value;
|
||||
self.signals().on_change().emit(value);
|
||||
}
|
||||
self.last_value = self.value;
|
||||
}
|
||||
|
||||
if let Some(label) = &mut self.label
|
||||
&& let Some(slider) = &self.slider
|
||||
{
|
||||
label.set_text(&format!("{}", slider.get_value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl SliderContainer {
|
||||
#[signal]
|
||||
pub fn on_change(value: f64);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user