1use std::collections::BTreeMap;
2
3use pfx_gpu::screens::DisplaySettings;
4use pfx_gpu::window::{FrameCap, FrameCaps};
5use pfx_input::BindingMap;
6use pfx_sound::{ChannelId, Mixer};
7use serde::{Deserialize, Serialize};
8
9#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
10#[serde(default)]
11pub struct Settings {
12 pub rumble: u8,
13 pub display: DisplaySettings,
14 pub pacing: Pacing,
15 pub audio: Audio,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub binds: Option<BindingMap>,
18}
19
20impl Default for Settings {
21 fn default() -> Self {
22 Self {
23 rumble: 100,
24 display: DisplaySettings::default(),
25 pacing: Pacing::default(),
26 audio: Audio::default(),
27 binds: None,
28 }
29 }
30}
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
33pub enum Cap {
34 #[serde(rename = "30")]
35 Fps30,
36 #[serde(rename = "60")]
37 Fps60,
38 #[serde(rename = "90")]
39 Fps90,
40 #[serde(rename = "120")]
41 Fps120,
42 #[serde(rename = "uncapped")]
43 Uncapped,
44}
45
46impl Cap {
47 pub fn frame_cap(self) -> FrameCap {
48 match self {
49 Cap::Fps30 => FrameCap::Fps30,
50 Cap::Fps60 => FrameCap::Fps60,
51 Cap::Fps90 => FrameCap::Fps90,
52 Cap::Fps120 => FrameCap::Fps120,
53 Cap::Uncapped => FrameCap::Uncapped,
54 }
55 }
56}
57
58impl From<FrameCap> for Cap {
59 fn from(cap: FrameCap) -> Self {
60 match cap {
61 FrameCap::Fps30 => Cap::Fps30,
62 FrameCap::Fps60 => Cap::Fps60,
63 FrameCap::Fps90 => Cap::Fps90,
64 FrameCap::Fps120 => Cap::Fps120,
65 FrameCap::Uncapped => Cap::Uncapped,
66 }
67 }
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
71#[serde(default)]
72pub struct Pacing {
73 pub focused: Cap,
74 pub background: Cap,
75 pub vrr: bool,
76 pub pause_when_minimized: bool,
77}
78
79impl Default for Pacing {
80 fn default() -> Self {
81 Self {
82 focused: Cap::Fps60,
83 background: Cap::Fps30,
84 vrr: false,
85 pause_when_minimized: true,
86 }
87 }
88}
89
90impl Pacing {
91 pub fn caps(&self) -> FrameCaps {
92 FrameCaps {
93 pause_when_minimized: self.pause_when_minimized,
94 ..FrameCaps::new(self.focused.frame_cap(), self.background.frame_cap())
95 }
96 }
97}
98
99#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
100#[serde(default)]
101pub struct Audio {
102 pub master: f32,
103 pub muted: bool,
104 pub music: f32,
105 pub effects: f32,
106 pub dialog: f32,
107 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
108 pub custom: BTreeMap<u32, f32>,
109}
110
111impl Default for Audio {
112 fn default() -> Self {
113 Self {
114 master: 1.0,
115 muted: false,
116 music: 1.0,
117 effects: 1.0,
118 dialog: 1.0,
119 custom: BTreeMap::new(),
120 }
121 }
122}
123
124impl Audio {
125 pub fn channels(&self) -> Vec<(ChannelId, f32)> {
126 let mut channels = vec![
127 (ChannelId::MUSIC, self.music),
128 (ChannelId::EFFECTS, self.effects),
129 (ChannelId::DIALOG, self.dialog),
130 ];
131 channels.extend(
132 self.custom
133 .iter()
134 .map(|(index, volume)| (ChannelId::custom(*index), *volume)),
135 );
136 channels
137 }
138
139 pub fn apply(&self, mixer: &mut Mixer) {
140 mixer.set_master_volume(self.master);
141 mixer.set_master_muted(self.muted);
142 for (channel, volume) in self.channels() {
143 mixer.set_channel_volume(channel, volume);
144 }
145 }
146}