ayaka_model/
view_model.rsuse crate::*;
use anyhow::Result;
use serde::Serialize;
use stream_future::stream;
use trylog::macros::*;
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "t", content = "data")]
pub enum OpenGameStatus {
LoadSettings,
LoadGlobalRecords,
LoadRecords,
Loaded,
}
pub struct GameViewModel<S: SettingsManager, M: RawModule + Send + Sync + 'static> {
context: Option<Context<M>>,
current_record: ActionRecord,
current_raw_context: Option<RawContext>,
settings_manager: S,
settings: Option<Settings>,
records: Vec<ActionRecord>,
global_record: Option<GlobalRecord>,
}
impl<S: SettingsManager, M: RawModule + Send + Sync + 'static> GameViewModel<S, M> {
pub fn new(settings_manager: S) -> Self {
Self {
settings_manager,
context: None,
current_record: ActionRecord::default(),
current_raw_context: None,
settings: None,
records: vec![],
global_record: None,
}
}
#[stream(OpenGameStatus, lifetime = 'a)]
pub async fn open_game<'a>(&'a mut self, context: Context<M>) -> Result<()> {
yield OpenGameStatus::LoadSettings;
let settings = unwrap_or_default_log!(
self.settings_manager.load_settings(),
"Load settings failed"
);
self.settings = Some(settings);
yield OpenGameStatus::LoadGlobalRecords;
let global_record = unwrap_or_default_log!(
self.settings_manager
.load_global_record(&context.game().config.title),
"Load global records failed"
);
self.global_record = Some(global_record);
yield OpenGameStatus::LoadRecords;
self.records = unwrap_or_default_log!(
self.settings_manager
.load_records(&context.game().config.title),
"Load records failed"
);
self.context = Some(context);
yield OpenGameStatus::Loaded;
Ok(())
}
pub fn context(&self) -> &Context<M> {
self.context
.as_ref()
.expect("should be called after open_game")
}
pub fn context_mut(&mut self) -> &mut Context<M> {
self.context
.as_mut()
.expect("should be called after open_game")
}
pub fn record(&self) -> &ActionRecord {
&self.current_record
}
pub fn settings(&self) -> &Settings {
self.settings
.as_ref()
.expect("should be called after open_game")
}
pub fn set_settings(&mut self, settings: Settings) {
self.settings = Some(settings);
}
pub fn records(&self) -> &[ActionRecord] {
&self.records
}
pub fn global_record(&self) -> &GlobalRecord {
self.global_record
.as_ref()
.expect("should be called after open_game")
}
pub fn global_record_mut(&mut self) -> &mut GlobalRecord {
self.global_record
.as_mut()
.expect("should be called after open_game")
}
pub fn avaliable_locale(&self) -> impl Iterator<Item = &Locale> {
self.context().game().paras.keys()
}
pub fn init_new(&mut self) {
let ctx = self.context().game().start_context();
self.current_record = ActionRecord::default();
self.current_raw_context = None;
self.context_mut().set_context(ctx);
}
pub fn init_context(&mut self, record: ActionRecord) {
let mut ctx = record.last_ctx_with_game(self.context().game());
self.current_record = record;
self.current_raw_context = self.current_record.history.last().cloned();
log::debug!("Context: {:?}", self.current_raw_context);
ctx.cur_act += 1;
self.context_mut().set_context(ctx);
}
pub fn init_context_by_index(&mut self, index: usize) {
self.init_context(self.records()[index].clone())
}
fn push_history(&mut self, ctx: &RawContext) {
let cur_text = self
.context()
.game()
.find_para(
&self.context().game().config.base_lang,
&ctx.cur_base_para,
&ctx.cur_para,
)
.and_then(|p| p.texts.get(ctx.cur_act));
let is_text = cur_text
.map(|line| matches!(line, Line::Text(_)))
.unwrap_or_default();
if is_text {
self.current_record.history.push(ctx.clone());
}
}
pub fn next_run(&mut self) -> bool {
let ctx = self.context_mut().next_run();
if let Some(ctx) = &ctx {
self.push_history(ctx);
self.global_record_mut().update(ctx);
log::debug!("{:?}", ctx);
}
self.current_raw_context = ctx;
self.current_raw_context.is_some()
}
pub fn next_back_run(&mut self) -> bool {
if self.current_record.history.len() <= 1 {
log::debug!("No action in the history.");
false
} else {
self.current_record.history.pop();
self.current_raw_context = self.current_record.history.last().cloned();
debug_assert!(self.current_raw_context.is_some());
let mut ctx = self
.current_raw_context
.clone()
.expect("current raw context cannot be None");
ctx.cur_act += 1;
self.context_mut().set_context(ctx);
true
}
}
pub fn current_run(&self) -> Option<&RawContext> {
self.current_raw_context.as_ref()
}
pub fn current_title(&self) -> Option<&String> {
self.context()
.current_paragraph_title(&self.settings().lang)
}
pub fn current_action(&self) -> Option<Action> {
self.current_run().map(|raw_ctx| {
unwrap_or_default_log!(
self.context().get_action(&self.settings().lang, raw_ctx),
"Cannot get action"
)
})
}
pub fn current_actions(&self) -> Option<(Action, Option<Action>)> {
self.current_run().map(|raw_ctx| self.get_actions(raw_ctx))
}
fn get_actions(&self, raw_ctx: &RawContext) -> (Action, Option<Action>) {
let action = unwrap_or_default_log!(
self.context().get_action(&self.settings().lang, raw_ctx),
"Cannot get action"
);
let base_action = self.settings().sub_lang.as_ref().map(|sub_lang| {
unwrap_or_default_log!(
self.context().get_action(sub_lang, raw_ctx),
"Cannot get sub action"
)
});
(action, base_action)
}
pub fn switch(&mut self, i: usize) {
log::debug!("Switch {}", i);
self.context_mut().switch(i);
}
pub fn save_current_to(&mut self, index: usize) {
let record = self.current_record.clone();
if index >= self.records.len() {
self.records.push(record);
} else {
self.records[index] = record;
}
}
pub fn save_settings(&self) -> Result<()> {
let game = &self.context().game().config.title;
self.settings_manager.save_settings(self.settings())?;
self.settings_manager
.save_global_record(game, self.global_record())?;
self.settings_manager.save_records(game, self.records())?;
Ok(())
}
pub fn current_visited(&self) -> bool {
self.current_run()
.map(|ctx| self.global_record().visited(ctx))
.unwrap_or_default()
}
pub fn records_text(&self) -> impl Iterator<Item = ActionText> + '_ {
self.records().iter().map(|record| {
let raw_ctx = record
.last_ctx()
.expect("there should be at least one RawContext in the ActionRecord");
let action = unwrap_or_default_log!(
self.context().get_action(&self.settings().lang, raw_ctx),
"Cannot get action"
);
if let Action::Text(action) = action {
action
} else {
panic!("action in the record should be text action")
}
})
}
pub fn current_history(
&self,
) -> impl DoubleEndedIterator<Item = (Action, Option<Action>)> + '_ {
self.record()
.history
.iter()
.map(|raw_ctx| self.get_actions(raw_ctx))
}
}