Skip to content

Commit

Permalink
feat: provide sort by attacks (melee, ranged, spellcaster) and roles …
Browse files Browse the repository at this point in the history
…with threshold
  • Loading branch information
RakuJa committed Dec 13, 2024
1 parent a5f251a commit 6e80064
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 108 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ sqlx = { version = "0.8.2", features = ["runtime-async-std", "sqlite"] }
cached = { version = "0.54.0", features = ["async"] }

anyhow = "1.0.94"
serde = { version = "1.0.215", features = ["derive"] }
serde = { version = "1.0.216", features = ["derive"] }
serde_json = "1.0.133"
strum = {version="0.26.3", features = ["derive"]}
fastrand = "2.3.0"
Expand Down
35 changes: 21 additions & 14 deletions src/db/bestiary_proxy.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::models::creature::creature_struct::Creature;
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};

use crate::db::data_providers::creature_fetcher::fetch_traits_associated_with_creatures;
use crate::db::data_providers::{creature_fetcher, generic_fetcher};
Expand Down Expand Up @@ -102,21 +102,28 @@ pub async fn get_paginated_creatures(
.essential
.alignment
.cmp(&b.core_data.essential.alignment),
CreatureSortEnum::Melee => a
CreatureSortEnum::Attacks => a
.core_data
.derived
.is_melee
.cmp(&b.core_data.derived.is_melee),
CreatureSortEnum::Ranged => a
.core_data
.derived
.is_ranged
.cmp(&b.core_data.derived.is_ranged),
CreatureSortEnum::SpellCaster => a
.core_data
.derived
.is_spell_caster
.cmp(&b.core_data.derived.is_spell_caster),
.attack_data
.cmp(&b.core_data.derived.attack_data),
CreatureSortEnum::Roles => {
let threshold = filters.role_threshold.unwrap_or(0);
a.core_data
.derived
.role_data
.iter()
.filter(|(_, y)| **y > threshold)
.collect::<BTreeMap<_, _>>()
.cmp(
&b.core_data
.derived
.role_data
.iter()
.filter(|(_, y)| **y > threshold)
.collect::<BTreeMap<_, _>>(),
)
}
};
match pagination
.bestiary_sort_data
Expand Down
10 changes: 4 additions & 6 deletions src/models/bestiary_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,10 @@ pub enum CreatureSortEnum {
Family,
#[serde(alias = "alignment", alias = "ALIGNMENT")]
Alignment,
#[serde(alias = "melee", alias = "MELEE")]
Melee,
#[serde(alias = "ranged", alias = "RANGED")]
Ranged,
#[serde(alias = "SPELLCASTER", alias = "SPELLCASTER")]
SpellCaster,
#[serde(alias = "attacks", alias = "ATTACKS")]
Attacks,
#[serde(alias = "roles", alias = "ROLES")]
Roles,
}

#[derive(Serialize, Deserialize, IntoParams, ToSchema, Eq, PartialEq, Hash, Default)]
Expand Down
62 changes: 34 additions & 28 deletions src/models/creature/creature_component/creature_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ use crate::models::creature::creature_metadata::type_enum::CreatureTypeEnum;
use crate::models::shared::rarity_enum::RarityEnum;
use crate::models::shared::size_enum::SizeEnum;
use serde::{Deserialize, Serialize};
#[allow(unused_imports)]
use serde_json::json;
use sqlx::sqlite::SqliteRow;
use sqlx::{Error, FromRow, Row};
use std::collections::BTreeMap;
use utoipa::ToSchema;

#[derive(Serialize, Deserialize, Clone, ToSchema, Eq, Hash, PartialEq)]
Expand Down Expand Up @@ -36,24 +39,10 @@ pub struct EssentialData {
pub struct DerivedData {
pub archive_link: Option<String>,

pub is_melee: bool,
pub is_ranged: bool,
pub is_spell_caster: bool,

#[schema(example = 50)]
pub brute_percentage: i64,
#[schema(example = 50)]
pub magical_striker_percentage: i64,
#[schema(example = 50)]
pub skill_paragon_percentage: i64,
#[schema(example = 50)]
pub skirmisher_percentage: i64,
#[schema(example = 50)]
pub sniper_percentage: i64,
#[schema(example = 50)]
pub soldier_percentage: i64,
#[schema(example = 50)]
pub spell_caster_percentage: i64,
#[schema(example = json!({"melee": true, "ranged": false, "spellcaster": true}))]
pub attack_data: BTreeMap<String, bool>,
#[schema(example = json!({"brute": 50, "magical_striker": 30, "skill_paragon": 2, "skirmisher": 3, "sniper": 0, "soldier": 30, "spellcaster": 90}))]
pub role_data: BTreeMap<String, i64>,
}

impl<'r> FromRow<'r, SqliteRow> for EssentialData {
Expand Down Expand Up @@ -81,18 +70,35 @@ impl<'r> FromRow<'r, SqliteRow> for EssentialData {

impl<'r> FromRow<'r, SqliteRow> for DerivedData {
fn from_row(row: &'r SqliteRow) -> Result<Self, Error> {
let mut attack_list = BTreeMap::new();
attack_list.insert(String::from("melee"), row.try_get("is_melee")?);
attack_list.insert(String::from("ranged"), row.try_get("is_ranged")?);
attack_list.insert(String::from("spellcaster"), row.try_get("is_spell_caster")?);

let mut role_list = BTreeMap::new();
role_list.insert(String::from("brute"), row.try_get("brute_percentage")?);
role_list.insert(
String::from("magical_striker"),
row.try_get("magical_striker_percentage")?,
);
role_list.insert(
String::from("skill_paragon"),
row.try_get("skill_paragon_percentage")?,
);
role_list.insert(
String::from("skirmisher"),
row.try_get("skirmisher_percentage")?,
);
role_list.insert(String::from("sniper"), row.try_get("sniper_percentage")?);
role_list.insert(String::from("soldier"), row.try_get("soldier_percentage")?);
role_list.insert(
String::from("spellcaster"),
row.try_get("spell_caster_percentage")?,
);
Ok(Self {
archive_link: row.try_get("archive_link").ok(),
is_melee: row.try_get("is_melee")?,
is_ranged: row.try_get("is_ranged")?,
is_spell_caster: row.try_get("is_spell_caster")?,
brute_percentage: row.try_get("brute_percentage")?,
magical_striker_percentage: row.try_get("magical_striker_percentage")?,
skill_paragon_percentage: row.try_get("skill_paragon_percentage")?,
skirmisher_percentage: row.try_get("skirmisher_percentage")?,
sniper_percentage: row.try_get("sniper_percentage")?,
soldier_percentage: row.try_get("soldier_percentage")?,
spell_caster_percentage: row.try_get("spell_caster_percentage")?,
attack_data: attack_list,
role_data: role_list,
})
}
}
Expand Down
91 changes: 56 additions & 35 deletions src/models/creature/creature_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,42 +128,63 @@ impl Creature {
}) && filters.alignment_filter.as_ref().map_or(true, |x| {
x.iter()
.any(|align| self.core_data.essential.alignment == *align)
}) && filters
.is_melee_filter
.map_or(true, |is_melee| self.core_data.derived.is_melee == is_melee)
&& filters.is_ranged_filter.map_or(true, |is_ranged| {
self.core_data.derived.is_ranged == is_ranged
})
&& filters
.is_spell_caster_filter
.map_or(true, |is_spell_caster| {
self.core_data.derived.is_spell_caster == is_spell_caster
}) && filters.attack_data_filter.clone().map_or(true, |attacks| {
self.core_data.derived.attack_data == attacks
}) && filters.type_filter.as_ref().map_or(true, |x| {
x.iter()
.any(|cr_type| self.core_data.essential.cr_type == *cr_type)
}) && (filters.role_threshold.is_none()
|| filters.role_filter.as_ref().map_or(true, |cr_role| {
let t = filters.role_threshold.unwrap_or(0);
cr_role.iter().any(|role| match role {
CreatureRoleEnum::Brute => {
self.core_data.derived.role_data.get("brute").unwrap_or(&0) >= &t
}
CreatureRoleEnum::MagicalStriker => {
self.core_data
.derived
.role_data
.get("magical_striker")
.unwrap_or(&0)
>= &t
}
CreatureRoleEnum::SkillParagon => {
self.core_data
.derived
.role_data
.get("skill_paragon")
.unwrap_or(&0)
>= &t
}
CreatureRoleEnum::Skirmisher => {
self.core_data
.derived
.role_data
.get("skirmisher")
.unwrap_or(&0)
>= &t
}
CreatureRoleEnum::Sniper => {
self.core_data.derived.role_data.get("sniper").unwrap_or(&0) >= &t
}
CreatureRoleEnum::Soldier => {
self.core_data
.derived
.role_data
.get("soldier")
.unwrap_or(&0)
>= &t
}
CreatureRoleEnum::SpellCaster => {
self.core_data
.derived
.role_data
.get("spellcaster")
.unwrap_or(&0)
>= &t
}
})
&& filters.type_filter.as_ref().map_or(true, |x| {
x.iter()
.any(|cr_type| self.core_data.essential.cr_type == *cr_type)
})
&& (filters.role_threshold.is_none()
|| filters.role_filter.as_ref().map_or(true, |cr_role| {
let t = filters.role_threshold.unwrap_or(0);
cr_role.iter().any(|role| match role {
CreatureRoleEnum::Brute => self.core_data.derived.brute_percentage >= t,
CreatureRoleEnum::MagicalStriker => {
self.core_data.derived.magical_striker_percentage >= t
}
CreatureRoleEnum::SkillParagon => {
self.core_data.derived.skill_paragon_percentage >= t
}
CreatureRoleEnum::Skirmisher => {
self.core_data.derived.skirmisher_percentage >= t
}
CreatureRoleEnum::Sniper => self.core_data.derived.sniper_percentage >= t,
CreatureRoleEnum::Soldier => self.core_data.derived.soldier_percentage >= t,
CreatureRoleEnum::SpellCaster => {
self.core_data.derived.spell_caster_percentage >= t
}
})
}))
}))
&& match filters.pathfinder_version.clone().unwrap_or_default() {
PathfinderVersionEnum::Legacy => !self.core_data.essential.remaster,
PathfinderVersionEnum::Remaster => self.core_data.essential.remaster,
Expand Down
5 changes: 2 additions & 3 deletions src/models/encounter_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::models::shared::size_enum::SizeEnum;
use serde::{Deserialize, Serialize};
#[allow(unused_imports)] // Used in schema
use serde_json::json;
use std::collections::HashMap;
use strum::EnumCount;
use strum::EnumIter;
use utoipa::ToSchema;
Expand All @@ -31,9 +32,7 @@ pub struct RandomEncounterData {
pub alignment_filter: Option<Vec<AlignmentEnum>>,
pub type_filter: Option<Vec<CreatureTypeEnum>>,
pub role_filter: Option<Vec<CreatureRoleEnum>>,
pub is_melee_filter: Option<bool>,
pub is_ranged_filter: Option<bool>,
pub is_spellcaster_filter: Option<bool>,
pub attack_list: Option<HashMap<String, bool>>,
pub role_lower_threshold: Option<u8>,
pub role_upper_threshold: Option<u8>,
pub challenge: Option<EncounterChallengeEnum>,
Expand Down
9 changes: 6 additions & 3 deletions src/models/routers_validator_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ use crate::models::pf_version_enum::PathfinderVersionEnum;
use crate::models::shared::rarity_enum::RarityEnum;
use crate::models::shared::size_enum::SizeEnum;
use serde::{Deserialize, Serialize};
#[allow(unused_imports)]
use serde_json::json;
use std::collections::BTreeMap;
use strum::Display;
use utoipa::{IntoParams, ToSchema};
#[derive(Serialize, Deserialize, IntoParams, ToSchema)]
Expand All @@ -30,9 +33,9 @@ pub struct CreatureFieldFilters {
pub min_level_filter: Option<i64>,
#[schema(minimum = -1, example = 5)]
pub max_level_filter: Option<i64>,
pub is_melee_filter: Option<bool>,
pub is_ranged_filter: Option<bool>,
pub is_spell_caster_filter: Option<bool>,

#[schema(example = json!({"melee": true, "ranged": false, "spellcaster": true}))]
pub attack_data_filter: Option<BTreeMap<String, bool>>,
pub pathfinder_version: Option<PathfinderVersionEnum>,
}

Expand Down
21 changes: 12 additions & 9 deletions src/services/encounter_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,18 @@ async fn calculate_random_encounter(
role_upper_threshold: enc_data
.role_upper_threshold
.unwrap_or(CreatureTableFieldsFilter::default_upper_threshold()),
is_melee_filter: enc_data
.is_melee_filter
.map_or_else(|| vec![true, false], |x| vec![x]),
is_ranged_filter: enc_data
.is_ranged_filter
.map_or_else(|| vec![true, false], |x| vec![x]),
is_spellcaster_filter: enc_data
.is_spellcaster_filter
.map_or_else(|| vec![true, false], |x| vec![x]),
is_melee_filter: enc_data.attack_list.as_ref().map_or_else(
|| vec![true, false],
|x| vec![*x.get("melee").unwrap_or(&false)],
),
is_ranged_filter: enc_data.attack_list.as_ref().map_or_else(
|| vec![true, false],
|x| vec![*x.get("ranged").unwrap_or(&false)],
),
is_spellcaster_filter: enc_data.attack_list.map_or_else(
|| vec![true, false],
|x| vec![*x.get("spellcaster").unwrap_or(&false)],
),
supported_version: enc_data
.pathfinder_version
.unwrap_or_default()
Expand Down
9 changes: 0 additions & 9 deletions src/services/url_calculator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,6 @@ fn creature_filter_query_calculator(field_filters: &CreatureFieldFilters) -> Str
field_filters
.max_level_filter
.map(|lvl| format!("max_level_filter={lvl}")),
field_filters
.is_melee_filter
.map(|is| format!("is_melee_filter={is}")),
field_filters
.is_ranged_filter
.map(|is| format!("is_ranged_filter={is}")),
field_filters
.is_spell_caster_filter
.map(|is| format!("is_spell_caster_filter={is}")),
]
.iter()
.filter_map(std::clone::Clone::clone)
Expand Down

0 comments on commit 6e80064

Please sign in to comment.