From a99ee46d26e309a4df6afc8a36faaa7b2135dee4 Mon Sep 17 00:00:00 2001 From: insects Date: Tue, 11 Feb 2025 15:45:57 +0100 Subject: [PATCH] support fisher's intuition --- README.md | 2 +- src/changelog.rs | 7 ++ src/data.rs | 17 +++- src/main.rs | 2 +- src/templates.rs | 215 ++++++++++++++++++++++++------------------- static/intuition.png | Bin 0 -> 732 bytes static/style.css | 49 ++++++++++ 7 files changed, 194 insertions(+), 98 deletions(-) create mode 100644 static/intuition.png diff --git a/README.md b/README.md index 04fec3d..65fbc86 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The data for fish is based on the data used in [Carbuncle Plushy's Fish Tracker] Current major missing features include: -- [ ] Fisher's Intution (catching other fish to spawn a big fish) +- [x] Fisher's Intuition (catching other fish to spawn a big fish) - [ ] Spearfishing - [x] Better condition display, show the weather, etc - [ ] Folklore requirement display diff --git a/src/changelog.rs b/src/changelog.rs index 2d76685..22ff6d3 100644 --- a/src/changelog.rs +++ b/src/changelog.rs @@ -6,6 +6,13 @@ pub fn changelog_page() -> Markup { layout(html! { h1 { "Beacon Changelog" } + section { + h2 { "1.4.0" } + ul { + li { "Implemented support for Fisher's Intuition" } + } + } + section { h2 { "1.3.0, 11.02.2025" } ul { diff --git a/src/data.rs b/src/data.rs index 2b776d7..7d8c119 100644 --- a/src/data.rs +++ b/src/data.rs @@ -189,6 +189,7 @@ pub struct CombinedFish<'a> { pub is_always_up: bool, pub windows: Vec, pub rarity: f32, + pub filtered: bool, } #[derive(Debug, Clone, Copy)] @@ -490,6 +491,7 @@ impl Data { let mut cfish = CombinedFish { entry: v, meta: m, + filtered: false, is_up: false, // fake default values for now is_always_up: false, // dito windows: Vec::new(), // dito @@ -528,9 +530,10 @@ impl Filters { &'a self, fish: Vec<&'a CombinedFish>, caught_fish_ids: &[i32], - ) -> Vec<&'a CombinedFish> { + ) -> Vec { fish.into_iter() - .filter(|fish| { + .cloned() + .map(|fish| { let f_caught = if self.include_caught { true } else { @@ -549,7 +552,10 @@ impl Filters { true }; - f_caught && f_patch && f_big + CombinedFish { + filtered: !(f_caught && f_patch && f_big), + ..fish + } }) .collect() } @@ -566,3 +572,8 @@ pub fn get_weather_icon(data: &Data, id: &u32) -> String { pub fn get_zone_name(data: &Data, id: u32) -> &str { &data.db_data.zones.get(&id).unwrap().name_en } + +pub fn display_intuition_length(length: u32) -> String { + let dur = Duration::seconds(length as i64); + format!("{}m{}s", dur.num_minutes(), dur.num_seconds() % 60) +} diff --git a/src/main.rs b/src/main.rs index 1305bc0..eb2b25d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,7 +85,7 @@ async fn main_handler( // Extract the list of fish we want to render. let meta = state.data.fish_with_meta(); - let mut values: Vec<&CombinedFish> = filters.filter(meta.values().collect(), &caught_fish); + let mut values: Vec = filters.filter(meta.values().collect(), &caught_fish); values.sort_by(|afish, bfish| { pinned_fish .contains(&(bfish.entry.id as i32)) diff --git a/src/templates.rs b/src/templates.rs index 1847c0a..695b853 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -11,7 +11,7 @@ use crate::{ pub struct ViewData<'a> { pub state: State>, - pub fish: Vec<&'a CombinedFish<'a>>, + pub fish: Vec>, pub caught_fish: Vec, pub pinned_fish: Vec, pub acc_id: String, @@ -75,129 +75,158 @@ pub fn main_page(data: ViewData) -> Markup { pub fn fish_list(data: &ViewData) -> Markup { html! { @for fish in data.fish.clone() { - section.fish.up[fish.is_up].alwaysup[fish.is_always_up].pinned[data.pinned_fish.contains(&(fish.entry.id as i32))] { - .title { - div { - @if !data.caught_fish.contains(&(fish.entry.id as i32)) { - form action=(format!("/{}/catch/{}", data.acc_id, fish.entry.id)) method="post" { - button.catch-button type="submit" { (PreEscaped("✓")) } + @if !fish.filtered { + (fish_display(&fish, data)) + @if !fish.entry.predators.is_empty() { + .predators { + div.predators-header { small { "⇖ Requirements" } } + @for predator in &fish.entry.predators { + @if let Some(pred_fish) = data.fish.iter().find(|f| f.entry.id == predator[0]) { + .predators-fish { + .amount { (predator[1]) } + (fish_display(pred_fish, data)) + } } } - - @if !data.pinned_fish.contains(&(fish.entry.id as i32)) { - form action=(format!("/{}/pin/{}", data.acc_id, fish.entry.id)) method="post" { - button.pin-button type="submit" { (PreEscaped("☆"))} - } - } @else { - form action=(format!("/{}/pin/{}/delete", data.acc_id, fish.entry.id)) method="post" { - button.pin-button type="submit" { (PreEscaped("★"))} - } - } - } - div { - h3 { - (fish.meta.name_en) - span class=(format!("patch patch-{}", fish.entry.patch as u32)) { (fish.entry.patch) } - } - .subtitle { - span { "Rarity: " (format!("{:.2}", fish.rarity * 100.)) "%" } - } } } - .when { - @if let Some(window) = fish.windows.first() { - @if fish.is_up { - "closes " (window.display_end_time()) - } @else { - "opens " (window.display_start_time()) - } - br; - @if fish.is_up { - .date data-ts=(clock::to_earth_time(window.start_time + window.duration).timestamp_millis()) { - .inner id=(format!("date-{}", fish.entry.id)) hx-preserve { - (clock::to_earth_time(window.start_time + window.duration).format("%c %Z")) - } - } + } + } + } +} - @if let Some(window2) = fish.windows.get(1) { - .date.tiny data-ts=(clock::to_earth_time(window2.start_time).timestamp_millis()) { - "next: " - .inner id=(format!("nextwindow-{}", fish.entry.id)) hx-preserve { - (clock::to_earth_time(window2.start_time).format("%c %Z")) - } - } - } - } @else { - .date data-ts=(clock::to_earth_time(window.start_time).timestamp_millis()) { - .inner id=(format!("date-{}", fish.entry.id)) hx-preserve { - (clock::to_earth_time(window.start_time).format("%c %Z")) - } - } +pub fn fish_display(fish: &CombinedFish, data: &ViewData) -> Markup { + html! { + section.fish.up[fish.is_up].alwaysup[fish.is_always_up].pinned[data.pinned_fish.contains(&(fish.entry.id as i32))] { + .title { + div { + @if !data.caught_fish.contains(&(fish.entry.id as i32)) { + form action=(format!("/{}/catch/{}", data.acc_id, fish.entry.id)) method="post" { + button.catch-button type="submit" { (PreEscaped("✓")) } } } + @if !data.pinned_fish.contains(&(fish.entry.id as i32)) { + form action=(format!("/{}/pin/{}", data.acc_id, fish.entry.id)) method="post" { + button.pin-button type="submit" { (PreEscaped("☆"))} + } + } @else { + form action=(format!("/{}/pin/{}/delete", data.acc_id, fish.entry.id)) method="post" { + button.pin-button type="submit" { (PreEscaped("★"))} + } + } } - .how { - @for item_id in &fish.entry.best_catch_path { - @if let Some(item) = data.state.data.db_data.items.get(item_id) { - span.catchpath title=(item.name_en) { - img src=(item.get_icon_url()) width="35"; + div.name { + h3 { + (fish.meta.name_en) + span class=(format!("patch patch-{}", fish.entry.patch as u32)) { (fish.entry.patch) } - @if let Some(hookset) = item.get_hookset(&data.state.data) { - img.hookset src=(hookset) width="20"; - } + .subtitle { + span { "Rarity: " (format!("{:.2}", fish.rarity * 100.)) "%" } + } + } - @if let Some(tug) = item.get_tug(&data.state.data) { - span.tug { (tug) } + @if let Some(length) = fish.entry.intuition_length { + .intuition { + img src="/static/intuition.png"; + div { (data::display_intuition_length(length)) } + } + } + } + } + .when { + @if let Some(window) = fish.windows.first() { + @if fish.is_up { + "closes " (window.display_end_time()) + } @else { + "opens " (window.display_start_time()) + } + br; + @if fish.is_up { + .date data-ts=(clock::to_earth_time(window.start_time + window.duration).timestamp_millis()) { + .inner id=(format!("date-{}", fish.entry.id)) hx-preserve { + (clock::to_earth_time(window.start_time + window.duration).format("%c %Z")) + } + } + + @if let Some(window2) = fish.windows.get(1) { + .date.tiny data-ts=(clock::to_earth_time(window2.start_time).timestamp_millis()) { + "next: " + .inner id=(format!("nextwindow-{}", fish.entry.id)) hx-preserve { + (clock::to_earth_time(window2.start_time).format("%c %Z")) } } } + } @else { + .date data-ts=(clock::to_earth_time(window.start_time).timestamp_millis()) { + .inner id=(format!("date-{}", fish.entry.id)) hx-preserve { + (clock::to_earth_time(window.start_time).format("%c %Z")) + } + } } - @if let Some(hookset) = fish.entry.hookset { - span.catchpath { - img src=(hookset) width="35"; + } - @if let Some(tug) = fish.entry.tug { + } + .how { + @for item_id in &fish.entry.best_catch_path { + @if let Some(item) = data.state.data.db_data.items.get(item_id) { + span.catchpath title=(item.name_en) { + img src=(item.get_icon_url()) width="35"; + + @if let Some(hookset) = item.get_hookset(&data.state.data) { + img.hookset src=(hookset) width="20"; + } + + @if let Some(tug) = item.get_tug(&data.state.data) { span.tug { (tug) } } } - } } - .meta { - @if let Some(location_id) = fish.entry.location { - @if let Some(location) = data.state.data.db_data.fishing_spots.get(&location_id) { - div { - span.zone { (fish.meta.zone_en) } - (location.name_en) - } + @if let Some(hookset) = fish.entry.hookset { + span.catchpath { + img src=(hookset) width="35"; + + @if let Some(tug) = fish.entry.tug { + span.tug { (tug) } } } - @if fish.entry.start_hour.is_some() && fish.entry.end_hour.is_some() { + } + } + .meta { + @if let Some(location_id) = fish.entry.location { + @if let Some(location) = data.state.data.db_data.fishing_spots.get(&location_id) { div { - @if !fish.is_always_up { - "ET " + span.zone { (fish.meta.zone_en) } + (location.name_en) + } + } + } + + @if fish.entry.start_hour.is_some() && fish.entry.end_hour.is_some() { + div { + @if !fish.is_always_up { + "ET " (clock::display_eorzea_time(&clock::set_hm_from_float(&clock::get_current_eorzea_date(), fish.entry.start_hour.unwrap()))) - "-" - (clock::display_eorzea_time(&clock::set_hm_from_float(&clock::get_current_eorzea_date(), fish.entry.end_hour.unwrap()))) + "-" + (clock::display_eorzea_time(&clock::set_hm_from_float(&clock::get_current_eorzea_date(), fish.entry.end_hour.unwrap()))) } @else { "always up!" - } } - div { - @if !fish.entry.weather_set.is_empty() { - @if !fish.entry.previous_weather_set.is_empty() { - @for weather in &fish.entry.previous_weather_set { - img src=(get_weather_icon(&data.state.data, weather)) width="20" title=(get_weather_name(&data.state.data, weather)); - } - - " ➞ " - } - - @for weather in &fish.entry.weather_set { + } + div { + @if !fish.entry.weather_set.is_empty() { + @if !fish.entry.previous_weather_set.is_empty() { + @for weather in &fish.entry.previous_weather_set { img src=(get_weather_icon(&data.state.data, weather)) width="20" title=(get_weather_name(&data.state.data, weather)); } + + " ➞ " + } + + @for weather in &fish.entry.weather_set { + img src=(get_weather_icon(&data.state.data, weather)) width="20" title=(get_weather_name(&data.state.data, weather)); } } } diff --git a/static/intuition.png b/static/intuition.png new file mode 100644 index 0000000000000000000000000000000000000000..6e12759d7010f2657f549a9702ccabc0c0587a34 GIT binary patch literal 732 zcmV<20wev2P)Px#1ZP1_K>z@;j|==^1poj7hDk(0RCwC$mSNG`Fbsx21}+1aK|cl=1CN0rcnmxU zf*=TjU zSiOP!x{zf4nLxv%(K%wbageaEm7P?b!cir}!r+VDkgQV*@t4$U)}z2e9h8NQwOGi8 zjZ?LA&@g0S2wlS$MEY6~X=vCgae;LKW9NQdHC_pZ3)lmvAQY5va0$4iKXAb#fiVO& zLCCOl5ujO3sN~OUOi7)BgAVM@S3$wqg%D{7Jn?#$yb!<>0W=jTs{kDuJGgQY9t3mm zr@$YyOH|NT0nX<&5dj?uU@4y4G#<8(WRs!_vSOk15n0LQ+;ct-(R!OLBpRcqF z@R`8w!g!DbumDAcDp^DU`|U*t1rIvVP*@}$G$j_0VGnM1V7FkKuXHR3hD&gf7=eZg zEF=&c(eQZ}Md3l9R+lXJcCDfe-VI94aM=_t9r(vcE^x`pDzWVfsp_=}P7;mf2j?Wv zmJ8=5lL{Ts?ttdwl`L#2s{l3I1a3mN@`nu1Iv@Qt{Qi)$W&J