1use crate::*;
2use anyhow::Result;
3use serde::{de::DeserializeOwned, Deserialize, Serialize};
4use std::{
5 collections::HashMap,
6 path::{Path, PathBuf},
7};
8
9#[derive(Debug, Clone, Deserialize, Serialize)]
11pub struct Settings {
12 pub lang: Locale,
14 pub sub_lang: Option<Locale>,
16 pub bgm_volume: u8,
18 pub voice_volume: u8,
20 pub video_volume: u8,
22}
23
24impl Default for Settings {
25 fn default() -> Self {
26 Self {
27 lang: Locale::default(),
28 sub_lang: None,
29 bgm_volume: 100,
30 voice_volume: 100,
31 video_volume: 100,
32 }
33 }
34}
35
36#[derive(Debug, Default, Clone, Deserialize, Serialize)]
38pub struct GlobalRecord {
39 pub record: HashMap<String, usize>,
42}
43
44impl GlobalRecord {
45 pub fn visited(&self, ctx: &RawContext) -> bool {
48 if let Some(max_act) = self.record.get(&ctx.cur_para) {
49 log::debug!("Test act: {}, max act: {}", ctx.cur_act, max_act);
50 *max_act >= ctx.cur_act
51 } else {
52 false
53 }
54 }
55
56 pub fn update(&mut self, ctx: &RawContext) {
58 self.record
59 .entry(ctx.cur_para.clone())
60 .and_modify(|act| *act = (*act).max(ctx.cur_act))
61 .or_insert(ctx.cur_act);
62 }
63}
64
65#[derive(Debug, Default, Clone, Deserialize, Serialize)]
67pub struct ActionRecord {
68 pub history: Vec<RawContext>,
70}
71
72impl ActionRecord {
73 pub fn last_ctx(&self) -> Option<&RawContext> {
75 self.history.last()
76 }
77
78 pub fn last_ctx_with_game(&self, game: &Game) -> RawContext {
81 self.last_ctx()
82 .cloned()
83 .unwrap_or_else(|| game.start_context())
84 }
85}
86
87pub trait SettingsManager {
92 fn load_file<T: DeserializeOwned>(&self, path: impl AsRef<Path>) -> Result<T>;
94
95 fn save_file<T: Serialize>(&self, path: impl AsRef<Path>, data: &T, pretty: bool)
97 -> Result<()>;
98
99 fn settings_path(&self) -> Result<PathBuf>;
101
102 fn load_settings(&self) -> Result<Settings> {
104 self.load_file(self.settings_path()?)
105 }
106
107 fn save_settings(&self, data: &Settings) -> Result<()> {
109 self.save_file(self.settings_path()?, data, true)
110 }
111
112 fn global_record_path(&self, game: &str) -> Result<PathBuf>;
114
115 fn load_global_record(&self, game: &str) -> Result<GlobalRecord> {
117 self.load_file(self.global_record_path(game)?)
118 }
119
120 fn save_global_record(&self, game: &str, data: &GlobalRecord) -> Result<()> {
122 self.save_file(self.global_record_path(game)?, data, false)
123 }
124
125 fn records_path(&self, game: &str) -> Result<impl Iterator<Item = Result<PathBuf>>>;
127
128 fn record_path(&self, game: &str, i: usize) -> Result<PathBuf>;
130
131 fn load_records(&self, game: &str) -> Result<Vec<ActionRecord>> {
133 self.records_path(game)?
134 .map(|path| path.and_then(|path| self.load_file(path)))
135 .collect()
136 }
137
138 fn save_records(&self, game: &str, contexts: &[ActionRecord]) -> Result<()> {
140 for (i, ctx) in contexts.iter().enumerate() {
141 self.save_file(self.record_path(game, i)?, ctx, false)?;
142 }
143 Ok(())
144 }
145}