From 67fe54c40ca12e1e80d92258488b220800a3e76e Mon Sep 17 00:00:00 2001 From: Loong Date: Thu, 9 May 2024 00:39:17 +0800 Subject: [PATCH 1/2] feat: add support for depot and operbox recognition --- maa-cli/src/command.rs | 8 ++ maa-cli/src/main.rs | 2 + maa-cli/src/run/callback/mod.rs | 89 +++++++++++++++++--- maa-cli/src/run/callback/summary.rs | 125 ++++++++++++++++++++++++++++ maa-cli/src/run/preset/mod.rs | 38 +++++++++ 5 files changed, 249 insertions(+), 13 deletions(-) diff --git a/maa-cli/src/command.rs b/maa-cli/src/command.rs index 2262b964..02ab9d5c 100644 --- a/maa-cli/src/command.rs +++ b/maa-cli/src/command.rs @@ -162,6 +162,14 @@ pub(crate) enum Command { #[command(flatten)] common: run::CommonArgs, }, + Depot { + #[command(flatten)] + common: run::CommonArgs, + }, + OperBox { + #[command(flatten)] + common: run::CommonArgs, + }, /// Convert file format between TOML, YAML and JSON /// /// This command will convert a file from TOML, YAML or JSON format to another format. diff --git a/maa-cli/src/main.rs b/maa-cli/src/main.rs index 95b99ad2..9e2aa9bd 100644 --- a/maa-cli/src/main.rs +++ b/maa-cli/src/main.rs @@ -95,6 +95,8 @@ fn main() -> Result<()> { Command::Roguelike { theme, common } => { run::run(|_| run::preset::roguelike(theme), common)? } + Command::Depot { common } => run::run(|_| run::preset::depot(), common)?, + Command::OperBox { common } => run::run(|_| run::preset::oper_box(), common)?, Command::Convert { input, output, diff --git a/maa-cli/src/run/callback/mod.rs b/maa-cli/src/run/callback/mod.rs index 50faa32c..da55ef36 100644 --- a/maa-cli/src/run/callback/mod.rs +++ b/maa-cli/src/run/callback/mod.rs @@ -339,24 +339,87 @@ fn process_subtask_completed(_: &Map) -> Option<()> { } fn process_subtask_extra_info(message: &Map) -> Option<()> { let taskchain = message.get("taskchain")?.as_str()?; + let what = message.get("what")?.as_str()?; + let details = message.get("details")?; match taskchain { - "Depot" => info!( - "{}: {}", - "Depot", - serde_json::to_string_pretty(message).unwrap() - ), - "OperBox" => info!( - "{}: {}", - "OperBox", - serde_json::to_string_pretty(message).unwrap() - ), + "Depot" => { + if !details.get("done")?.as_bool()? { + return Some(()); + } + + let items = details + .get("arkplanner")? + .get("object")? + .get("items")? + .as_array()?; + + let mut all_items = summary::Map::new(); + + for item in items { + let name = item.get("name")?.as_str()?; + let have = item.get("have")?.as_i64()?; + all_items.insert(name.to_owned(), have); + } + + debug!( + "Depot: {}", + serde_json::to_string_pretty(&all_items).unwrap() + ); + + edit_current_task_detail(|detail| { + if let Some(detail) = detail.as_depot_mut() { + detail.set_depot(all_items); + } + }); + } + "OperBox" => { + if !details.get("done")?.as_bool()? { + return Some(()); + } + + let all_opers = details.get("all_opers")?.as_array()?; + let owned_opers = details.get("own_opers")?.as_array()?; + + use summary::OperInfo; + + let mut opers_by_rarity = Vec::with_capacity(6); + for _ in 0..6 { + let opers = summary::Map::>::new(); + opers_by_rarity.push(opers); + } + + for oper in all_opers { + let oper_info = oper.as_object()?; + let name = oper_info.get("name")?.as_str()?; + let rarity = oper_info.get("rarity")?.as_i64()? as usize; + opers_by_rarity[rarity - 1].insert(name.to_owned(), None); + } + + for oper in owned_opers { + let oper_info = oper.as_object()?; + let name = oper_info.get("name")?.as_str()?; + let rarity = oper_info.get("rarity")?.as_i64()? as usize; + + opers_by_rarity[rarity - 1] + .get_mut(name) + .unwrap() + .replace(OperInfo { + potential: oper_info.get("potential")?.as_i64()?, + elite: oper_info.get("elite")?.as_i64()?, + level: oper_info.get("level")?.as_i64()?, + }); + } + + edit_current_task_detail(|detail| { + if let Some(detail) = detail.as_operbox_mut() { + detail.set_operbox(opers_by_rarity); + } + }); + } _ => {} } - let what = message.get("what")?.as_str()?; - let details = message.get("details")?; - match what { "StageDrops" => { let drops = details.get("drops")?.as_array()?; diff --git a/maa-cli/src/run/callback/summary.rs b/maa-cli/src/run/callback/summary.rs index a33e8356..bd83ec5a 100644 --- a/maa-cli/src/run/callback/summary.rs +++ b/maa-cli/src/run/callback/summary.rs @@ -116,6 +116,8 @@ impl TaskSummary { Infrast => Detail::Infrast(InfrastDetail::new()), Recruit => Detail::Recruit(RecruitDetail::new()), Roguelike => Detail::Roguelike(RoguelikeDetail::new()), + Depot => Detail::Depot(DepotDetail::new()), + OperBox => Detail::OperBox(OperBoxDetail::new()), _ => Detail::None, }; @@ -243,6 +245,8 @@ pub enum Detail { Fight(FightDetail), Recruit(RecruitDetail), Roguelike(RoguelikeDetail), + Depot(DepotDetail), + OperBox(OperBoxDetail), } impl Detail { @@ -277,6 +281,22 @@ impl Detail { None } } + + pub fn as_depot_mut(&mut self) -> Option<&mut DepotDetail> { + if let Detail::Depot(detail) = self { + Some(detail) + } else { + None + } + } + + pub fn as_operbox_mut(&mut self) -> Option<&mut OperBoxDetail> { + if let Detail::OperBox(detail) = self { + Some(detail) + } else { + None + } + } } impl std::fmt::Display for Detail { @@ -287,6 +307,8 @@ impl std::fmt::Display for Detail { Detail::Infrast(detail) => detail.fmt(f)?, Detail::Recruit(detail) => detail.fmt(f)?, Detail::Roguelike(detail) => detail.fmt(f)?, + Detail::Depot(detail) => detail.fmt(f)?, + Detail::OperBox(detail) => detail.fmt(f)?, } Ok(()) @@ -798,6 +820,68 @@ impl std::fmt::Display for ExplorationDetail { } } +pub struct DepotDetail(Option>); + +impl DepotDetail { + pub fn new() -> Self { + Self(None) + } + + pub fn set_depot(&mut self, map: Map) { + self.0 = Some(map); + } +} + +impl std::fmt::Display for DepotDetail { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(map) = &self.0 { + writeln!(f, "ITEM\tCOUNT")?; + for (item, count) in map { + writeln!(f, "{}\t{}", item, count)?; + } + } + Ok(()) + } +} + +pub struct OperBoxDetail(Vec>>); + +impl OperBoxDetail { + pub fn new() -> Self { + Self(Vec::with_capacity(6)) + } + + pub fn set_operbox(&mut self, operbox: Vec>>) { + self.0.extend(operbox); + } +} + +impl std::fmt::Display for OperBoxDetail { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "NAME\tPOTENTIAL\tELITE\tLEVEL")?; + for map in self.0.iter().rev() { + if !map.is_empty() { + for (name, info) in map { + write!(f, "{}\t", name)?; + if let Some(info) = info { + write!(f, "{}\t{}\t{}", info.potential, info.elite, info.level)?; + } else { + write!(f, "\t\t")?; + } + writeln!(f)?; + } + } + } + Ok(()) + } +} + +pub struct OperInfo { + pub(super) potential: i64, + pub(super) elite: i64, + pub(super) level: i64, +} + pub fn insert_or_add_by_ref(map: &mut Map, key: &str, value: i64) { if let Some(old) = map.get_mut(key) { *old += value; @@ -1139,5 +1223,46 @@ mod tests { Total gained 300 exp\n", ); } + + #[test] + fn depot() { + let mut detail = DepotDetail::new(); + detail.set_depot( + [("A", 1), ("B", 2)] + .into_iter() + .map(|(k, v)| (k.to_owned(), v)) + .collect(), + ); + assert_eq!(detail.to_string(), "ITEM\tCOUNT\nA\t1\nB\t2\n"); + } + + #[test] + fn operbox() { + let mut detail = OperBoxDetail::new(); + detail.set_operbox(vec![ + [( + "A", + Some(OperInfo { + potential: 1, + elite: 0, + level: 1, + }), + )] + .into_iter() + .map(|(k, v)| (k.to_owned(), v)) + .collect(), + [("B", None)] + .into_iter() + .map(|(k, v)| (k.to_owned(), v)) + .collect(), + ]); + assert_eq!( + detail.to_string(), + "NAME\tPOTENTIAL\tELITE\tLEVEL\n\ + B\t\t\t\n\ + A\t1\t0\t1\n\ + ", + ); + } } } diff --git a/maa-cli/src/run/preset/mod.rs b/maa-cli/src/run/preset/mod.rs index 49b1ef37..51bbe231 100644 --- a/maa-cli/src/run/preset/mod.rs +++ b/maa-cli/src/run/preset/mod.rs @@ -53,6 +53,22 @@ pub fn fight(stage: String, medicine: Option) -> Result { Ok(task_config) } +pub fn depot() -> Result { + let mut task_config = TaskConfig::new(); + + task_config.push(Task::new_with_default(Depot, object!())); + + Ok(task_config) +} + +pub fn oper_box() -> Result { + let mut task_config = TaskConfig::new(); + + task_config.push(Task::new_with_default(OperBox, object!())); + + Ok(task_config) +} + mod copilot; pub use copilot::copilot; @@ -156,4 +172,26 @@ mod tests { 1 ); } + + #[test] + fn test_depot() { + let task_config = depot().unwrap(); + let tasks = task_config.tasks(); + + assert_eq!(tasks.len(), 1); + let depot_task = tasks.first().unwrap(); + + assert_eq!(depot_task.task_type(), Depot); + } + + #[test] + fn test_oper_box() { + let task_config = oper_box().unwrap(); + let tasks = task_config.tasks(); + + assert_eq!(tasks.len(), 1); + let oper_box_task = tasks.first().unwrap(); + + assert_eq!(oper_box_task.task_type(), OperBox); + } } From d92e2099ccba3154f7b163de7b8133f3fabcf0dd Mon Sep 17 00:00:00 2001 From: Loong Date: Thu, 9 May 2024 00:44:52 +0800 Subject: [PATCH 2/2] test: add test for cli parser --- maa-cli/src/command.rs | 22 +++++++++++++++++++++- maa-cli/src/main.rs | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/maa-cli/src/command.rs b/maa-cli/src/command.rs index 02ab9d5c..7d7b8cbb 100644 --- a/maa-cli/src/command.rs +++ b/maa-cli/src/command.rs @@ -166,7 +166,7 @@ pub(crate) enum Command { #[command(flatten)] common: run::CommonArgs, }, - OperBox { + Operbox { #[command(flatten)] common: run::CommonArgs, }, @@ -681,6 +681,26 @@ mod test { ); } + #[test] + fn depot() { + assert_matches!( + parse_from(["maa", "depot"]).command, + Command::Depot { + common: run::CommonArgs { .. } + } + ); + } + + #[test] + fn operbox() { + assert_matches!( + parse_from(["maa", "operbox"]).command, + Command::Operbox { + common: run::CommonArgs { .. } + } + ); + } + #[test] fn convert() { assert_matches!( diff --git a/maa-cli/src/main.rs b/maa-cli/src/main.rs index 9e2aa9bd..db036b12 100644 --- a/maa-cli/src/main.rs +++ b/maa-cli/src/main.rs @@ -96,7 +96,7 @@ fn main() -> Result<()> { run::run(|_| run::preset::roguelike(theme), common)? } Command::Depot { common } => run::run(|_| run::preset::depot(), common)?, - Command::OperBox { common } => run::run(|_| run::preset::oper_box(), common)?, + Command::Operbox { common } => run::run(|_| run::preset::oper_box(), common)?, Command::Convert { input, output,