From 2c483810228991767cdf8939784427b05274cfbd Mon Sep 17 00:00:00 2001 From: augenfrosch <45207902+augenfrosch@users.noreply.github.com> Date: Fri, 23 Aug 2024 19:07:38 +0200 Subject: [PATCH] Add text icons for HQ items and collectables (#77) * Ignore collectable and HQ character during search * Add text icon font * Use HQ character in item name instead of "(HQ)" * Use collectable character in item name of collectables * Use `AlwaysCollectable` instead of `IsCollectable` entry from item game data * Move collectable name formating to `locales.rs` to make it global * Do not ignore collectable character since it is unnecessary to do so * Update tests using item names to account for changed behaviour * Use more readable way of removing null bytes from the search text * Do not ignore HQ character for food items and potions This makes the search more "correct" and consistent to the recipe search. Additionally the search for all HQ items of a category is now possible. * Replace "(HQ)" and "(CL)" with the HQ and collectable character respecively in the search text This allows for easy input of those special characters without pasting them directly. --- .../XIV_Icon_Recreations.ttf | Bin 0 -> 1828 bytes game_data/build/items.rs | 4 +-- game_data/build/records.rs | 4 +-- game_data/src/consumables.rs | 4 +-- game_data/src/lib.rs | 2 +- game_data/src/locales.rs | 16 ++++++++-- game_data/tests/test_items.rs | 30 +++++++++++++++--- src/app.rs | 13 ++++++++ src/widgets/food_select.rs | 4 ++- src/widgets/potion_select.rs | 4 ++- src/widgets/recipe_select.rs | 7 ++-- src/widgets/simulator.rs | 2 +- 12 files changed, 70 insertions(+), 20 deletions(-) create mode 100644 assets/fonts/XIV_Icon_Recreations/XIV_Icon_Recreations.ttf diff --git a/assets/fonts/XIV_Icon_Recreations/XIV_Icon_Recreations.ttf b/assets/fonts/XIV_Icon_Recreations/XIV_Icon_Recreations.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9d75786ca356e3f4c7be8409e02e5cbf6c7d56ea GIT binary patch literal 1828 zcmds2UuauZ82_DnZ<2PaospT~%*rkO%UrwL8|h|Eb#85zc3V5LQFL@%3@oqKP(q6mU-p36Ds z_x--_ob#RUe)qc&0O~Oh6{o}D6GuNSwmb$rHDm*a`uf9o7TW>$uTma9zQ^o78$Aq& z3CgDv)A5AFziLZ{>9~D(3;UnR;zJo|~yuxHH@jDc6iooge$@owsAuzexM` ziBvq<{>3LZsFVA5Pmu7uslH2{T;Dt~oj`{RHCJSbq}prBJC?ov9S)LlV+Ed6#O_A?R776s3pk zwP>)E(i_NI_{?@(Sd%OKUkK5DQ*2>ZCZ*FrqzfUa= zUHka`^x$(%=Aq%9boPy`Dr%ok4jmix`p%T^A3rdBu&{LXyLnA{_2?1p<;jbeuZFMW zPVCt6Qsnd9MxiOt@xtLVr$%ot-T3I#S^v(5eAOx%nEmgmqi|yjn1sU9_KGll{+6eN z@VZ*OEz0djk6tSb74Dbk%5&m^xLJ;rBjToFEG+ayB7wViS44feZe>NRuUKhNkLy;x z**T#&Sg@vaI9Oo>qYhSK;Fg0mY!`k9yRc0haj={G1)0TK^?R|gKs#9*E39Y-9L&0g zlMdEsv*utI8pLh~yU{LKF~CwXfDyzvCXhjzD2fzumSY@wOj>M?h65w9fkY-9i>4CU zR6IYKNt2bvJ?!?e`o^*PKOAdYn+P&` z``AtHV1l(YoJr@yne2EpNraJ9Hph_YhSAk&7$p8xH&P@z25hlQqt+($(FpJ%7CSeV5#N^FQHFSYjdh literal 0 HcmV?d00001 diff --git a/game_data/build/items.rs b/game_data/build/items.rs index 0d6744f..916d28e 100644 --- a/game_data/build/items.rs +++ b/game_data/build/items.rs @@ -14,10 +14,10 @@ pub fn import_item_records( .filter(|item| relevant_items.contains(&item.id)) { item_stats.entry(item.id, &format!( - "Item {{ item_level: {item_level}, can_be_hq: {can_be_hq}, is_collectable: {is_collectable} }}", + "Item {{ item_level: {item_level}, can_be_hq: {can_be_hq}, always_collectable: {always_collectable} }}", item_level = item.item_level, can_be_hq = item.can_be_hq, - is_collectable = item.is_collectable, + always_collectable = item.always_collectable, )); } let out_path = Path::new(&std::env::var("OUT_DIR")?).join("items.rs"); diff --git a/game_data/build/records.rs b/game_data/build/records.rs index c3a24f0..c7431dd 100644 --- a/game_data/build/records.rs +++ b/game_data/build/records.rs @@ -80,9 +80,9 @@ pub struct ItemRecord { #[serde(rename = "CanBeHq")] #[serde(deserialize_with = "bool_string")] pub can_be_hq: bool, - #[serde(rename = "IsCollectable")] + #[serde(rename = "AlwaysCollectable")] #[serde(deserialize_with = "bool_string")] - pub is_collectable: bool, + pub always_collectable: bool, } #[derive(Deserialize)] diff --git a/game_data/src/consumables.rs b/game_data/src/consumables.rs index 8ce7f16..b82ed54 100644 --- a/game_data/src/consumables.rs +++ b/game_data/src/consumables.rs @@ -103,7 +103,7 @@ mod tests { #[test] fn test_u16_overflow() { - let consumable = find_consumable("Rroneek Steak (HQ)").unwrap(); + let consumable = find_consumable("Rroneek Steak \u{e03c}").unwrap(); // 13108 * 5 mod 1<<16 = 4 // 2521 * 26 mod 1<<16 = 10 assert_eq!( @@ -114,7 +114,7 @@ mod tests { #[test] fn test_rroneek_steak_hq() { - let consumable = find_consumable("Rroneek Steak (HQ)").unwrap(); + let consumable = find_consumable("Rroneek Steak \u{e03c}").unwrap(); assert_eq!( consumable.effect_string(4021, 4023, 550), "Control +5% (97), CP +26% (92)" diff --git a/game_data/src/lib.rs b/game_data/src/lib.rs index e65ffae..758187f 100644 --- a/game_data/src/lib.rs +++ b/game_data/src/lib.rs @@ -14,7 +14,7 @@ use simulator::{Action, ActionMask, Settings}; pub struct Item { pub item_level: u16, pub can_be_hq: bool, - pub is_collectable: bool, + pub always_collectable: bool, } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] diff --git a/game_data/src/locales.rs b/game_data/src/locales.rs index 46abf88..42bda07 100644 --- a/game_data/src/locales.rs +++ b/game_data/src/locales.rs @@ -1,3 +1,4 @@ +use crate::ITEMS; use serde::{Deserialize, Serialize}; use simulator::Action; @@ -61,9 +62,18 @@ pub fn get_item_name(item_id: u32, hq: bool, locale: Locale) -> String { .copied() .unwrap_or("Unknown item"), }; - match hq { - true => format!("{} (HQ)", item_name), - false => item_name.to_string(), + let item_entry = ITEMS.get(&item_id); + let always_collectable = match item_entry { + Some(item) => item.always_collectable, + None => false, + }; + if !always_collectable { + match hq { + true => format!("{} \u{e03c}", item_name), + false => item_name.to_string(), + } + } else { + format!("{} \u{e03d}", item_name) } } diff --git a/game_data/tests/test_items.rs b/game_data/tests/test_items.rs index 32d45e2..2f45c21 100644 --- a/game_data/tests/test_items.rs +++ b/game_data/tests/test_items.rs @@ -33,8 +33,28 @@ fn test_item_name_2341() { } #[test] -fn test_item_name_44232_hq() { +fn test_item_name_44232_collectable() { let item_id = 44232; + let item_names = [ + get_item_name(item_id, false, Locale::EN), + get_item_name(item_id, true, Locale::DE), + get_item_name(item_id, false, Locale::FR), + get_item_name(item_id, true, Locale::JP), + ]; + assert_eq!( + item_names, + [ + "Rarefied Tacos de Carne Asada \u{e03d}", + "Tacos de Carne Asada (Sammlerstück) \u{e03d}", + "Tacos de carne asada collectionnables \u{e03d}", + "収集用のタコス・カルネ・アサーダ \u{e03d}" + ] + ); +} + +#[test] +fn test_item_name_44104_hq() { + let item_id = 44104; let item_names = [ get_item_name(item_id, true, Locale::EN), get_item_name(item_id, true, Locale::DE), @@ -44,10 +64,10 @@ fn test_item_name_44232_hq() { assert_eq!( item_names, [ - "Rarefied Tacos de Carne Asada (HQ)", - "Tacos de Carne Asada (Sammlerstück) (HQ)", - "Tacos de carne asada collectionnables (HQ)", - "収集用のタコス・カルネ・アサーダ (HQ)" + "Tacos de Carne Asada \u{e03c}", + "Tacos mit Carne Asada \u{e03c}", + "Tacos de carne asada \u{e03c}", + "タコス・カルネ・アサーダ \u{e03c}" ] ); } diff --git a/src/app.rs b/src/app.rs index 6f64d33..573d0c6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -637,6 +637,19 @@ impl MacroSolverApp { .get_mut(&FontFamily::Monospace) .unwrap() .push("japanese_monospace".to_owned()); + + fonts.font_data.insert( + String::from("FFXIV_Lodestone_SSF"), + FontData::from_static(include_bytes!( + "../assets/fonts/XIV_Icon_Recreations/XIV_Icon_Recreations.ttf" + )), + ); + fonts + .families + .get_mut(&FontFamily::Proportional) + .unwrap() + .push("FFXIV_Lodestone_SSF".to_owned()); + ctx.set_fonts(fonts); } } diff --git a/src/widgets/food_select.rs b/src/widgets/food_select.rs index 5ec7013..319aa06 100644 --- a/src/widgets/food_select.rs +++ b/src/widgets/food_select.rs @@ -83,7 +83,9 @@ impl<'a> Widget for FoodSelect<'a> { ui.horizontal(|ui| { ui.label("Search:"); - ui.text_edit_singleline(&mut search_text); + if ui.text_edit_singleline(&mut search_text).changed() { + search_text = search_text.replace("\0", "").replace("(HQ)", "\u{e03c}"); + } }); ui.separator(); diff --git a/src/widgets/potion_select.rs b/src/widgets/potion_select.rs index aa7006a..4bb28d2 100644 --- a/src/widgets/potion_select.rs +++ b/src/widgets/potion_select.rs @@ -83,7 +83,9 @@ impl<'a> Widget for PotionSelect<'a> { ui.horizontal(|ui| { ui.label("Search:"); - ui.text_edit_singleline(&mut search_text); + if ui.text_edit_singleline(&mut search_text).changed() { + search_text = search_text.replace("\0", "").replace("(HQ)", "\u{e03c}"); + } }); ui.separator(); diff --git a/src/widgets/recipe_select.rs b/src/widgets/recipe_select.rs index 194f349..686d4cf 100644 --- a/src/widgets/recipe_select.rs +++ b/src/widgets/recipe_select.rs @@ -68,14 +68,17 @@ impl<'a> RecipeSelect<'a> { ui.horizontal(|ui| { ui.label("Search:"); - ui.text_edit_singleline(&mut search_text); + if ui.text_edit_singleline(&mut search_text).changed() { + search_text = search_text.replace("\0", "").replace("(CL)", "\u{e03d}"); + } }); ui.separator(); let mut search_result = Vec::new(); ui.ctx().memory_mut(|mem| { let search_cache = mem.caches.cache::>(); - search_result = search_cache.get((&search_text.to_lowercase(), self.locale)); + let cache_search_string = search_text.trim_end_matches('\u{e03c}').to_lowercase(); + search_result = search_cache.get((&cache_search_string, self.locale)); }); ui.ctx().data_mut(|data| { diff --git a/src/widgets/simulator.rs b/src/widgets/simulator.rs index dcb6de4..6aa2bf3 100644 --- a/src/widgets/simulator.rs +++ b/src/widgets/simulator.rs @@ -153,7 +153,7 @@ impl<'a> Widget for Simulator<'a> { egui::RichText::new("Synthesis failed".to_string()) .strong(), ); - } else if self.item.is_collectable { + } else if self.item.always_collectable { let t1 = QualityTarget::CollectableT1 .get_target(self.settings.max_quality); let t2 = QualityTarget::CollectableT2