From 87ca8c6ec8784f06c19188974357dfeb4edbfa89 Mon Sep 17 00:00:00 2001 From: yanweitong Date: Tue, 22 Aug 2023 00:01:59 +0800 Subject: [PATCH] auto search shiny done. --- Cargo.lock | 191 ++++++++++++++++++++++++++++- Cargo.toml | 13 +- src/joystick/joystick.rs | 58 +++++++++ src/joystick/joystick_test.rs | 17 +++ src/joystick/mod.rs | 2 + src/lib.rs | 3 +- src/ocr/mod.rs | 2 + src/ocr/ocr.rs | 65 ++++++++++ src/ocr/ocr_test.rs | 44 +++++++ src/pokemmo/const_value.rs | 117 ++++++++++++++++++ src/pokemmo/pokemmo.rs | 225 ++++++++++++++++++++++++++++++++-- src/pokemmo/pokemmo_test.rs | 84 ++++++++----- src/pokemon/pokemon.rs | 145 +++++++++++++++++++++- src/screen/screen.rs | 5 +- src/screen/screen_test.rs | 4 +- 15 files changed, 917 insertions(+), 58 deletions(-) create mode 100644 src/joystick/joystick.rs create mode 100644 src/joystick/joystick_test.rs create mode 100644 src/ocr/mod.rs create mode 100644 src/ocr/ocr.rs create mode 100644 src/ocr/ocr_test.rs diff --git a/Cargo.lock b/Cargo.lock index d90f07e..87ae83e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,6 +241,27 @@ dependencies = [ "windows", ] +[[package]] +name = "errno" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "exr" version = "1.7.0" @@ -257,6 +278,12 @@ dependencies = [ "zune-inflate", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "fdeflate" version = "0.3.0" @@ -400,6 +427,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "lebe" version = "0.5.2" @@ -422,6 +455,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" + [[package]] name = "lock_api" version = "0.4.10" @@ -434,9 +473,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "malloc_buf" @@ -500,7 +539,11 @@ dependencies = [ "crossterm", "enigo", "image", + "log", + "rand", + "rusty-tesseract", "screenshots", + "simple-logging", ] [[package]] @@ -576,7 +619,7 @@ checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", "windows-targets", ] @@ -626,6 +669,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.66" @@ -662,6 +711,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "rayon" version = "1.7.0" @@ -684,6 +763,12 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + [[package]] name = "redox_syscall" version = "0.3.5" @@ -693,6 +778,32 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "rustix" +version = "0.38.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rusty-tesseract" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2176026d24dbb67968571c7d464d287934820490ece864e17b88d2e5e3eee8" +dependencies = [ + "image", + "subprocess", + "substring", + "tempfile", + "thiserror", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -753,6 +864,17 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simple-logging" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00d48e85675326bb182a2286ea7c1a0b264333ae10f27a937a72be08628b542" +dependencies = [ + "lazy_static", + "log", + "thread-id", +] + [[package]] name = "smallvec" version = "1.11.0" @@ -768,6 +890,25 @@ dependencies = [ "lock_api", ] +[[package]] +name = "subprocess" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "substring" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" +dependencies = [ + "autocfg", +] + [[package]] name = "syn" version = "2.0.28" @@ -779,6 +920,50 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread-id" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" +dependencies = [ + "libc", + "redox_syscall 0.1.57", + "winapi", +] + [[package]] name = "tiff" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index be9cf42..a8dca2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,10 +10,10 @@ edition = "2021" default = ["pokemmo"] -pokemmo = ["screen","ocr","joystick","dep:image","pokemon","detector"] +pokemmo = ["screen", "ocr", "joystick", "dep:image", "pokemon", "detector"] screen = ["dep:screenshots"] -ocr = [] -joystick = ["dep:enigo"] +ocr = ["dep:rusty-tesseract", "dep:image"] +joystick = ["dep:enigo","dep:rand"] pokemon = [] detector = ["dep:crossterm"] @@ -26,5 +26,8 @@ path = "src/lib.rs" log = "0.4.20" enigo = { version = "0.1.2", optional = true } screenshots = { version = "0.7.0", optional = true } -image = {version = "0.24.6", optional = true} -crossterm = {version = "0.27.0", optional = true} \ No newline at end of file +image = { version = "0.24.6", optional = true } +crossterm = { version = "0.27.0", optional = true } +rusty-tesseract = { version = "1.1.7", optional = true } +rand = { version = "0.8.5", optional = true } +simple-logging = {version = "2.0.2"} \ No newline at end of file diff --git a/src/joystick/joystick.rs b/src/joystick/joystick.rs new file mode 100644 index 0000000..b7cca75 --- /dev/null +++ b/src/joystick/joystick.rs @@ -0,0 +1,58 @@ +#[cfg(feature = "joystick")] +pub(crate) mod joystick{ + use std::thread; + use std::time::Duration; + use enigo::{Enigo, Key, KeyboardControllable}; + use rand::Rng; + + pub enum MoveMode{ + // 横向 + HORIZONTAL, + // 纵向 + PORTRAIT, + // 随机 + RANDOM, + } + + impl MoveMode { + pub fn to_key_list(&self) -> Vec{ + return match self { + MoveMode::HORIZONTAL => vec![Key::Layout('j'),Key::Layout('l')], + MoveMode::PORTRAIT => vec![Key::UpArrow,Key::DownArrow], + MoveMode:: RANDOM => vec![Key::LeftArrow,Key::RightArrow,Key::UpArrow,Key::DownArrow], + } + } + } + + + pub(crate) fn move_once_by_mode(road : &MoveMode, index : usize){ + let mut enigo = Enigo::new(); + let moves = road.to_key_list(); + let move_index = index % moves.len(); + let move_key = moves[move_index]; + long_press(enigo,move_key,1000,500); + } + + pub(crate) fn once_press(mut enigo:Enigo, key: Key){ + enigo.key_click(key); + } + + pub(crate) fn quick_press(key: Key){ + let mut enigo = Enigo::new(); + let mut rng = rand::thread_rng(); + enigo.key_down(key); + thread::sleep(Duration::from_millis(rng.gen_range(0 .. 20))); + enigo.key_up(key); + } + + pub(crate) fn long_press(mut enigo:Enigo, key: Key, est_time: isize, random_time: isize){ + let mut rng = rand::thread_rng(); + enigo.key_down(key); + let act = est_time + rng.gen_range((-random_time .. random_time)); + thread::sleep(Duration::from_millis(act as u64)); + enigo.key_up(key); + } + + + +} \ No newline at end of file diff --git a/src/joystick/joystick_test.rs b/src/joystick/joystick_test.rs new file mode 100644 index 0000000..dcef34e --- /dev/null +++ b/src/joystick/joystick_test.rs @@ -0,0 +1,17 @@ +#[cfg(test)] +mod tests { + use crate::joystick::joystick::joystick::{move_once_by_mode, MoveMode}; + + #[test] + fn try_move() { + let mut count = 0; + let mode = MoveMode::HORIZONTAL; + while count < 100 { + move_once_by_mode(&mode, 0); + count = count + 1; + } + + } + + +} \ No newline at end of file diff --git a/src/joystick/mod.rs b/src/joystick/mod.rs index e69de29..a8d05bb 100644 --- a/src/joystick/mod.rs +++ b/src/joystick/mod.rs @@ -0,0 +1,2 @@ +pub mod joystick; +pub mod joystick_test; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 12625f1..dcf96c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,4 +2,5 @@ pub(crate) mod screen; pub(crate) mod joystick; pub(crate) mod pokemmo; pub(crate) mod pokemon; -pub(crate) mod detector; \ No newline at end of file +pub(crate) mod detector; +pub(crate) mod ocr; \ No newline at end of file diff --git a/src/ocr/mod.rs b/src/ocr/mod.rs new file mode 100644 index 0000000..78e2004 --- /dev/null +++ b/src/ocr/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod ocr; +mod ocr_test; \ No newline at end of file diff --git a/src/ocr/ocr.rs b/src/ocr/ocr.rs new file mode 100644 index 0000000..b620334 --- /dev/null +++ b/src/ocr/ocr.rs @@ -0,0 +1,65 @@ +#[cfg(feature = "ocr")] +pub(crate) mod ocr { + use std::error::Error; + use image::DynamicImage; + use rusty_tesseract::{Args, Image}; + + /** + + OEM 解释 + 0 仅旧版引擎 + 1 仅神经网络 LSTM 引擎 + 2 个旧版 + LSTM 引擎 + 3 默认,基于可用的内容 + + + PSM 解释 : + 0 仅方向和脚本检测 (OSD)。 + 1 使用 OSD 自动页面分割。 + 2 自动页面分割,但没有 OSD 或 OCR。 (未实现) + 3 全自动页面分割,但无OSD。 (默认) + 4 假设有一列大小可变的文本。 + 5 假设有一个统一的垂直对齐文本块。 + 6 假设有一个统一的文本块。 + 7 将图像视为单个文本行。 + 8 将图像视为单个单词。 + 9 将图像视为圆圈中的单个单词。 + 10 将图像视为单个字符。 + 11 稀疏文本。 查找尽可能多的文本(不按特定顺序排列)。 + 12 带 OSD 的稀疏文本。 + 13 原始线。 将图像视为单个文本行, + + */ + + pub(crate) fn find_string_in_image(image: &DynamicImage, psm:i32) -> String { + let img = Image::from_dynamic_image(image).expect("from image failed"); + let mut args = Args { + lang: "chi_sim".to_string(), + config_variables: Default::default(), + dpi: Some(300), + psm: Some(psm), + oem: Some(3), + }; + let output = rusty_tesseract::image_to_string(&img, &args).expect("image to string failed"); + return output; + } + + + pub(crate) fn find_text_in_image(key_words: &Vec, image: &DynamicImage) -> bool { + let img = Image::from_dynamic_image(image).expect("from image failed"); + let mut args = Args { + lang: "chi_sim".to_string(), + config_variables: Default::default(), + dpi: Some(600), + psm: Some(6), + oem: Some(3), + }; + let output = rusty_tesseract::image_to_string(&img, &args).expect("image to string failed"); + for word in key_words{ + if output.contains(word){ + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/ocr/ocr_test.rs b/src/ocr/ocr_test.rs new file mode 100644 index 0000000..641bee8 --- /dev/null +++ b/src/ocr/ocr_test.rs @@ -0,0 +1,44 @@ +#[cfg(test)] +mod tests { + use std::collections::HashMap; + use image::io::Reader; + use rusty_tesseract::{Args, Image}; + use crate::ocr::ocr::ocr::find_text_in_image; + + + #[test] + fn test_ocr_image() { + println!("ocr !"); + + // let img = Image::from_path("test/image/group_5.png").expect("image not found"); + let img = Image::from_path("test/image/sb.png").expect("image not found"); + let mut args = Args { + lang: "chi_sim".to_string(), + config_variables: Default::default(), + dpi: Some(300), + psm: Some(1), + oem: Some(3), + }; + + // string output + let output = rusty_tesseract::image_to_string(&img, &args).expect("ocr error!"); + println!("The String output is: {:?}", output); + + } + + #[test] + fn test_ocr_dynamic_image() { + let dynamic_image = Reader::open("test/image/1.png") + .unwrap() + .decode() + .unwrap(); + let key_words = vec!["头目".to_string(),"白银山".to_string()]; + let has_word = find_text_in_image(&key_words,&dynamic_image); + + println!("含有关键词:{}",has_word); + + } + + + +} \ No newline at end of file diff --git a/src/pokemmo/const_value.rs b/src/pokemmo/const_value.rs index e5306ce..c9a7c02 100644 --- a/src/pokemmo/const_value.rs +++ b/src/pokemmo/const_value.rs @@ -7,25 +7,142 @@ pub mod pokemmo_const_value{ right: 80, top: 0, bottom: 20, + psm : 3, }; pub(crate) const MAP_CITY: Area = Area{ left: 0, right: 250, top: 24, bottom: 55, + psm : 7, }; pub(crate) const SHORTCUT_KEY_1: Area = Area{ left: 255, right: 298, top: 28, bottom: 70, + psm : 8, }; pub(crate) const SHORTCUT_KEY_5: Area = Area{ left: 445, right: 485, top: 28, bottom: 70, + psm : 8, }; + pub(crate) const TEXT_AREA: Area = Area{ + left: 0, + right: 800, + top: 1020, + bottom: 1340, + psm : 6, + }; + // 战斗对话狂 + pub(crate) const TEMP_BATTLE_TEXT_AREA: Area = Area{ + left: 400, + right: 1100, + top: 850, + bottom: 1020, + psm : 6, + }; + pub(crate) const LAST_TEXT_AREA: Area = Area{ + left: 0, + right: 800, + top: 1295, + bottom: 1340, + psm : 7, + }; + pub(crate) const SINGLE_BATTLE: Area = Area{ + left: 400, + right: 800, + top: 180, + bottom: 260, + psm : 3, + }; + pub(crate) const GROUP_5_1: Area = Area{ + left: 700, + right: 1050, + top: 90, + bottom: 148, + psm : 3, + }; + pub(crate) const GROUP_5_2: Area = Area{ + left: 1100, + right: 1400, + top: 90, + bottom: 148, + psm : 3, + }; + pub(crate) const GROUP_5_3: Area = Area{ + left: 1500, + right: 1800, + top: 90, + bottom: 148, + psm : 3, + }; + pub(crate) const GROUP_5_4: Area = Area{ + left: 740, + right: 1050, + top: 170, + bottom: 210, + psm : 3, + }; + pub(crate) const GROUP_5_5: Area = Area{ + left: 1500, + right: 1800, + top: 170, + bottom: 210, + psm : 3, + }; + + pub(crate) const BUTTON_HL_1: Area = Area{ + left: 392, + right: 406, + top: 863, + bottom: 878, + psm : 3, + }; + pub(crate) const BUTTON_HL_2: Area = Area{ + left: 708, + right: 722, + top: 863, + bottom: 878, + psm : 3, + }; + pub(crate) const BUTTON_HL_3: Area = Area{ + left: 392, + right: 406, + top: 942, + bottom: 957, + psm : 3, + }; + pub(crate) const BUTTON_HL_4: Area = Area{ + left: 708, + right: 722, + top: 942, + bottom: 957, + psm : 3, + }; + + + #[derive(Debug)] + pub enum PokemmoStatus { + // 遇见闪光 + MeetShiny, + // 无状态 + Free, + // 加载遇怪动画 + LoadBattle, + // 遇怪状态 + InBattle, + // 逃跑中 + Running, + // 检查逃跑成功 + CheckRun, + + } + + } diff --git a/src/pokemmo/pokemmo.rs b/src/pokemmo/pokemmo.rs index 3ad705d..c50390c 100644 --- a/src/pokemmo/pokemmo.rs +++ b/src/pokemmo/pokemmo.rs @@ -1,34 +1,233 @@ #[cfg(feature = "pokemmo")] -pub mod pokemmo{ - use screenshots::Compression; - use crate::pokemmo::const_value::pokemmo_const_value::LOGO; - use crate::screen::screen::screen::{screen_shot}; +pub mod pokemmo { + use std::thread; + use std::time::Duration; + + use enigo::Key; + use enigo::Key::{N, V}; + use image::{DynamicImage, RgbaImage}; use image::io::Reader as ImageReader; + use log::{error, info, trace}; + use rand::thread_rng; + use rusty_tesseract::Image; + use screenshots::Compression; + + use crate::joystick::joystick::joystick::{move_once_by_mode, MoveMode, quick_press}; + use crate::ocr::ocr::ocr::find_string_in_image; + use crate::pokemmo::const_value::pokemmo_const_value::{BUTTON_HL_1, BUTTON_HL_2, BUTTON_HL_3, BUTTON_HL_4, GROUP_5_1, GROUP_5_2, GROUP_5_3, GROUP_5_4, GROUP_5_5, LAST_TEXT_AREA, LOGO, PokemmoStatus, SINGLE_BATTLE, TEMP_BATTLE_TEXT_AREA, TEXT_AREA}; + use crate::pokemmo::const_value::pokemmo_const_value::PokemmoStatus::{Free, InBattle, LoadBattle, MeetShiny, Running}; + use crate::screen::screen::screen::{Area, screen_shot}; + + // 单遇闪光 + pub fn single_meet_shiny(key_words: &Vec) { + let mut status = PokemmoStatus::Free; + let move_mode = MoveMode::HORIZONTAL; + + let mut move_index = 0_usize; + let mut meet_pokemmo: String = String::new(); + loop { + trace!("当前状态:{:?}",status); + match status { + PokemmoStatus::MeetShiny => { + return; + } + PokemmoStatus::Free => { + trace!("自由状态,移动一步"); + move_once_by_mode(&move_mode, move_index); + move_index = move_index + 1; + let text = get_last_string(); + trace!("查询战斗文字 : {}",text); + if text.contains("野生") || text.contains("派出") { + info!("遇怪:{}.",text); + meet_pokemmo = text.clone(); + status = LoadBattle; + }else { + let temp_battle = read_area(TEMP_BATTLE_TEXT_AREA); + if temp_battle.contains("战斗") || temp_battle.contains("逃跑"){ + status = LoadBattle; + } + } + } + PokemmoStatus::LoadBattle => { + info!("进入加载动画"); + let mut btn_index = None; + while btn_index == None { + trace!("等待按钮加载"); + let est_index = get_choose_btn(); + if est_index.is_some() { + btn_index = est_index; + status = InBattle; + } + thread::sleep(Duration::from_millis(100)); + } + } + PokemmoStatus::InBattle => { + trace!("检查遇怪种类.."); + let text = get_text_list(); + let filter_text: Vec = text.into_iter().filter(|x| x.contains("野生")).collect(); + if filter_text.is_empty() { + error!("遇怪错误!"); + return; + } + let filter_size = filter_text.len(); + let pokemon_text = filter_text[filter_size - 1].clone(); + if pokemon_text.contains("怪群") { + info!("遇见怪群"); + let five_names = read_group_5(); + for name in five_names { + for key_word in key_words { + if name.contains(key_word) { + status = MeetShiny; + return; + } + } + } + } else { + let name = read_area(SINGLE_BATTLE); + info!("单遇:{}",name); + for key_word in key_words { + if name.contains(key_word) || meet_pokemmo.contains(key_word) { + info!("遇见{}",key_word); + status = MeetShiny; + return; + } + } + } + // 进入招式界面 + status = PokemmoStatus::Running + } + PokemmoStatus::Running => { + thread::sleep(Duration::from_micros(100)); + quick_press(Key::Layout('l')); + thread::sleep(Duration::from_micros(100)); + quick_press(Key::Layout('k')); + thread::sleep(Duration::from_micros(100)); + let mut est_index = get_choose_btn(); + while est_index.is_none() || est_index.unwrap() != 3 { + quick_press(Key::Layout('l')); + quick_press(Key::Layout('k')); + trace!("尝试获取激活按钮"); + est_index = get_choose_btn(); + } + quick_press(Key::Layout('z')); + thread::sleep(Duration::from_micros(100)); + status = PokemmoStatus::CheckRun; + } + PokemmoStatus::CheckRun => { + let mut last_word = get_last_string(); + // 读取十次后默认成功 + let mut times = 0_usize; + while !last_word.contains("成功") + && !last_word.contains("中逃跑") + && !last_word.contains("不能跑") + && times < 10{ + trace!("读取逃跑文档 : {}",last_word); + last_word = get_last_string(); + thread::sleep(Duration::from_millis(200)); + times = times + 1; + } + if last_word.contains("成功") || last_word.contains("逃跑") { + trace!("逃跑成功."); + status = Free; + } else { + trace!("逃跑失败"); + status = Free; + } + } + } + } + } + + pub fn get_text_list() -> Vec { + let text_image = read_area(TEXT_AREA); + let text_list: Vec = text_image.split("\r\n").filter(|x| !x.is_empty()).map(|x| String::from(x)).collect(); + return text_list; + } + pub fn get_last_string() -> String { + let text_image = read_area(LAST_TEXT_AREA); + return text_image; + } - pub(crate) fn check_screen_active() -> bool{ - + pub(crate) fn check_screen_active() -> bool { let logo_image = screen_shot(Some(LOGO)); - let data= logo_image.to_png(Some(Compression::Default)).expect("image to png failed"); - + let data = logo_image.to_png(Some(Compression::Default)).expect("image to png failed"); let active_logo = ImageReader::open("./resources/pokemmo/image/logo_active.png").expect("read active logo image failed").decode().expect("decode image failed"); let active_logo_data = active_logo.as_bytes().to_vec(); let size = data.len(); let mut dis = 0_isize; - for i in 0 .. size{ - dis += active_logo_data[i].clone() as isize - data[i].clone() as isize; + for i in 0..size { + dis += active_logo_data[i.clone()].clone() as isize - data[i].clone() as isize; } - println!("dis : {}",dis); + println!("dis : {}", dis); return dis <= 5000; - - } + pub fn read_area(area: Area) -> String { + let psm = area.psm.clone(); + let image = screen_shot(Some(area)); + let image_data = image.rgba().clone(); + let image_width = image.width(); + let image_height = image.height(); + let dy_image = DynamicImage::from(RgbaImage::from_vec(image_width, image_height, image_data).unwrap()); + let text = find_string_in_image(&dy_image, psm); + return text; + } + pub fn read_group_5() -> Vec { + let mut result = Vec::new(); + result.push(read_area(GROUP_5_1)); + result.push(read_area(GROUP_5_2)); + result.push(read_area(GROUP_5_3)); + result.push(read_area(GROUP_5_4)); + result.push(read_area(GROUP_5_5)); + return result; + } + + + pub fn get_choose_btn() -> Option { + let mut btn_list = Vec::new(); + btn_list.push(screen_shot(Some(BUTTON_HL_1))); + btn_list.push(screen_shot(Some(BUTTON_HL_2))); + btn_list.push(screen_shot(Some(BUTTON_HL_3))); + btn_list.push(screen_shot(Some(BUTTON_HL_4))); + let mut y_max = 0_f32; + let mut max_index = None; + for index in 0..4 { + let image = &mut btn_list[index]; + let data = image.rgba(); + let size = data.len(); + let pixel_size = size / 4; + let mut r_total = 0_usize; + let mut g_total = 0_usize; + let mut b_total = 0_usize; + for i in 0..pixel_size { + r_total += (data[i.clone() * 4].clone() as usize); + g_total += (data[i.clone() * 4 + 1].clone() as usize); + b_total += (data[i.clone() * 4 + 2].clone() as usize); + } + let r_avg = r_total / pixel_size; + let g_avg = g_total / pixel_size; + let b_avg = b_total / pixel_size; + + let r1 = r_avg as f32 / 255_f32; + let g1 = g_avg as f32 / 255_f32; + let b1 = b_avg as f32 / 255_f32; + + let max = r1.max(g1).max(b1); + let k = 1_f32 - max; + let y = (1_f32 - b1 - k) / (1_f32 - k); + if y > y_max && y > 0.4_f32 { + y_max = y; + max_index = Some(index); + } + } + return max_index; + } } \ No newline at end of file diff --git a/src/pokemmo/pokemmo_test.rs b/src/pokemmo/pokemmo_test.rs index 10b0f05..472c5f1 100644 --- a/src/pokemmo/pokemmo_test.rs +++ b/src/pokemmo/pokemmo_test.rs @@ -3,52 +3,81 @@ mod tests { use std::thread::sleep; use std::time::Duration; use image::io::Reader as ImageReader; - use crate::pokemmo::const_value::pokemmo_const_value::{LOGO, MAP_CITY, SHORTCUT_KEY_1, SHORTCUT_KEY_5}; - use crate::pokemmo::pokemmo::pokemmo::check_screen_active; + use log::LevelFilter; + use crate::pokemmo::const_value::pokemmo_const_value::{BUTTON_HL_1, BUTTON_HL_2, BUTTON_HL_3, BUTTON_HL_4, GROUP_5_1, GROUP_5_2, GROUP_5_3, GROUP_5_4, GROUP_5_5, LAST_TEXT_AREA, LOGO, MAP_CITY, SHORTCUT_KEY_1, SHORTCUT_KEY_5, SINGLE_BATTLE, TEMP_BATTLE_TEXT_AREA, TEXT_AREA}; + use crate::pokemmo::pokemmo::pokemmo::{check_screen_active, get_choose_btn, get_last_string, read_area, read_group_5, single_meet_shiny}; use crate::screen::screen::screen::{print_image, screen_shot}; + #[test] - fn print_logo() { - sleep(Duration::from_secs(1)); + fn try_meet_shiny(){ + simple_logging::log_to_file("./test/log/meet_shiny.log",LevelFilter::Info).expect("set log failed"); - let logo_image = screen_shot(Some(LOGO)); - print_image(logo_image, "logo".to_string()); + let key_words = vec![ + "闪".to_string(), + "光".to_string(), + ]; + single_meet_shiny(&key_words); + + } - let active = check_screen_active(); - println!("main screen active : {}", active); + #[test] + fn print_shortcut() { + let text = screen_shot(Some(TEMP_BATTLE_TEXT_AREA)); + print_image(text, "temp_battle_text".to_string()); } #[test] - fn print_city() { - sleep(Duration::from_secs(1)); + fn print_group_5() { + let group_5_1 = screen_shot(Some(GROUP_5_1)); + print_image(group_5_1, "group_5_1".to_string()); + let group_5_2 = screen_shot(Some(GROUP_5_2)); + print_image(group_5_2, "group_5_2".to_string()); + let group_5_3 = screen_shot(Some(GROUP_5_3)); + print_image(group_5_3, "group_5_3".to_string()); + let group_5_4 = screen_shot(Some(GROUP_5_4)); + print_image(group_5_4, "group_5_4".to_string()); + let group_5_5 = screen_shot(Some(GROUP_5_5)); + print_image(group_5_5, "group_5_5".to_string()); + } + + #[test] + fn print_button() { + let btn_hl_1 = screen_shot(Some(BUTTON_HL_1)); + print_image(btn_hl_1, "btn_hl_1".to_string()); + let btn_hl_2 = screen_shot(Some(BUTTON_HL_2)); + print_image(btn_hl_2, "btn_hl_2".to_string()); + let btn_hl_3 = screen_shot(Some(BUTTON_HL_3)); + print_image(btn_hl_3, "btn_hl_3".to_string()); + let btn_hl_4 = screen_shot(Some(BUTTON_HL_4)); + print_image(btn_hl_4, "btn_hl_4".to_string()); - let logo_image = screen_shot(Some(MAP_CITY)); - print_image(logo_image, "city".to_string()); } #[test] - fn print_shortcut_key_1() { - sleep(Duration::from_secs(1)); - - let logo_image = screen_shot(Some(SHORTCUT_KEY_1)); - print_image(logo_image, "key_1".to_string()); - + fn find_text() { + let text = read_area(TEMP_BATTLE_TEXT_AREA); + println!("text in image: {}",text); } + #[test] - fn print_shortcut_key_5() { - sleep(Duration::from_secs(1)); - - let logo_image = screen_shot(Some(SHORTCUT_KEY_5)); - print_image(logo_image, "key_5".to_string()); - + fn get_active_btn() { + let index_op = get_choose_btn(); + if index_op.is_some(){ + println!("激活索引:{}",index_op.unwrap()); + }else { + println!("无激活索引"); + } } + + + #[test] fn compare_logo() { - let active_logo = ImageReader::open("./resources/pokemmo/image/logo_active.png").expect("read active logo image failed").decode().expect("decode image failed"); let active_logo_data = active_logo.as_bytes().to_vec(); @@ -59,11 +88,8 @@ mod tests { let mut dis = 0_isize; for i in 0 .. size{ - dis += na_logo_data[i].clone() as isize - active_logo_data[i].clone() as isize; + dis += na_logo_data[i.clone()].clone() as isize - active_logo_data[i.clone()].clone() as isize; } - println!("dis = {}",dis); - - } } \ No newline at end of file diff --git a/src/pokemon/pokemon.rs b/src/pokemon/pokemon.rs index 9679bba..2accdd0 100644 --- a/src/pokemon/pokemon.rs +++ b/src/pokemon/pokemon.rs @@ -1,4 +1,147 @@ #[cfg(feature = "pokemon")] -pub(crate) mod pokemon{ +pub(crate) mod pokemon { + pub enum PokeType { + Normal, + Fighting, + Flying, + Poison, + Ground, + Rock, + Bug, + Ghost, + Steel, + File, + Water, + Grass, + Electric, + Psychic, + Ice, + Dragon, + Dark, + Fairy, + } + pub struct Pokemon { + pub(crate) dex: PokeDex, + pub(crate) moves: Vec, + pub(crate) lv: u32, + pub(crate) stat: Stat, + pub(crate) status: StatusChange, + pub(crate) nature: Nature + } + + pub struct PokeDex { + pub(crate) no: u32, + pub(crate) fir_type: PokeType, + pub(crate) sec_type: Option, + pub(crate) iv: Iv, + } + + pub struct Iv { + pub(crate) hp: u32, + pub(crate) attack: u32, + pub(crate) defense: u32, + pub(crate) sp_atk: u32, + pub(crate) sp_def: u32, + pub(crate) speed: u32, + } + + pub struct Ev { + pub(crate) hp: u32, + pub(crate) attack: u32, + pub(crate) defense: u32, + pub(crate) sp_atk: u32, + pub(crate) sp_def: u32, + pub(crate) speed: u32, + } + + pub struct Stat { + pub(crate) hp: u32, + pub(crate) attack: u32, + pub(crate) defense: u32, + pub(crate) sp_atk: u32, + pub(crate) sp_def: u32, + pub(crate) speed: u32, + } + + pub struct StatusChange { + pub(crate) attack: i32, + pub(crate) defense: i32, + pub(crate) sp_atk: i32, + pub(crate) sp_def: i32, + pub(crate) speed: i32, + pub(crate) accuracy: i32, + pub(crate) evasion: i32, + } + + pub enum MoveCategory { + Physical, + Special, + Status, + } + + pub struct Move { + pub(crate) cat: MoveCategory, + pub(crate) value: u32, + } + + pub enum Nature { + Hardy, + Lonely, + Brave, + Adamant, + Naughty, + Bold, + Docile, + Relaxed, + Impish, + Lax, + Timid, + Hasty, + Serious, + Jolly, + Naive, + Modest, + Mild, + Quiet, + Bashful, + Rash, + Calm, + Gentle, + Sassy, + Careful, + Quirky, + } + + impl Nature { + pub fn influence(&self) -> (f32, f32, f32, f32, f32) { + return match self { + Hardy => (0_f32, 0_f32, 0_f32, 0_f32, 0_f32), + Lonely => (1.1_f32, 0.9_f32, 0_f32, 0_f32, 0_f32), + Brave => (1.1_f32, 0_f32, 0_f32, 0_f32, 0.9_f32), + Adamant => (1.1_f32, 0_f32, 0.9_f32, 0_f32, 0_f32), + Naughty => (1.1_f32, 0_f32, 0_f32, 0.9_f32, 0_f32), + Bold => (0.9_f32, 1.1_f32, 0_f32, 0_f32, 0_f32), + Docile => (0_f32, 0_f32, 0_f32, 0_f32, 0_f32), + Relaxed => (0_f32, 1.1_f32, 0_f32, 0_f32, 0.9_f32), + Impish => (0_f32, 1.1_f32, 0.9_f32, 0_f32, 0_f32), + Lax => (0_f32, 1.1_f32, 0_f32, 0.9_f32, 0_f32), + Timid => (0.9_f32, 0_f32, 0_f32, 0_f32, 1.1_f32), + Hasty => (0_f32, 0.9_f32, 0_f32, 0_f32, 1.1_f32), + Serious => (0_f32, 0_f32, 0_f32, 0_f32, 0_f32), + Jolly => (0_f32, 0_f32, 0.9_f32, 0_f32, 1.1_f32), + Naive => (0_f32, 0_f32, 0_f32, 0.9_f32, 1.1_f32), + Modest => (0.9_f32, 0_f32, 1.1_f32, 0_f32, 0_f32), + Mild => (0_f32, 0.9_f32, 1.1_f32, 0_f32, 0_f32), + Quiet => (0_f32, 0_f32, 1.1_f32, 0_f32, 0.9_f32), + Bashful => (0_f32, 0_f32, 0_f32, 0_f32, 0_f32), + Rash => (0_f32, 0_f32, 1.1_f32, 0.9_f32, 0_f32), + Calm => (0.9_f32, 0_f32, 0_f32, 1.1_f32, 0_f32), + Gentle => (0_f32, 0.9_f32, 0_f32, 1.1_f32, 0_f32), + Sassy => (0_f32, 0_f32, 0_f32, 1.1_f32, 0.9_f32), + Careful => (0_f32, 0_f32, 0.9_f32, 1.1_f32, 0_f32), + Quirky => (0_f32, 0_f32, 0_f32, 0_f32, 0_f32), + }; + } + } } \ No newline at end of file diff --git a/src/screen/screen.rs b/src/screen/screen.rs index fe0c06f..0638fa2 100644 --- a/src/screen/screen.rs +++ b/src/screen/screen.rs @@ -9,8 +9,10 @@ pub mod screen { pub right: i32, pub top: i32, pub bottom: i32, + pub psm : i32, } + pub fn screen_shot(area: Option) -> Image { let main_screen_list: Vec = Screen::all().expect("get all screen failed").into_iter().filter(|x| x.display_info.is_primary).collect(); let main_screen = main_screen_list.first().expect("get main screen failed"); @@ -25,12 +27,9 @@ pub mod screen { return image } - pub fn print_image(image: Image,file_name : String){ fs::write(format!("test/image/{}.png", file_name), image.to_png(Some(Compression::Default)).expect("image to png failed")).unwrap(); } - - } \ No newline at end of file diff --git a/src/screen/screen_test.rs b/src/screen/screen_test.rs index 14ee9e9..22df9b4 100644 --- a/src/screen/screen_test.rs +++ b/src/screen/screen_test.rs @@ -10,12 +10,10 @@ mod tests { right: 300, top: 0, bottom: 300, + psm: 3, }; let image = screen_shot(Some(area)); - print_image(image,"abc".to_string()); - - }