From cabe17dc64da4681c7ea848b889c8f1ca32c7d5a Mon Sep 17 00:00:00 2001 From: rakuja Date: Thu, 11 Jul 2024 20:15:59 +0200 Subject: [PATCH] feat: * add pwl for single creature fetch (base,weak,elite); * update dockerfile to avoid warnings * rename OptionalData to ResponseDataModifiers --- Dockerfile | 2 +- src/db/bestiary_proxy.rs | 13 ++++---- src/db/data_providers/creature_fetcher.rs | 19 +++++++---- .../creature_component/creature_combat.rs | 26 +++++++++++++++ .../creature_component/creature_extra.rs | 23 +++++++++++++ .../creature_spell_caster.rs | 23 +++++++++++++ src/models/creature/creature_struct.rs | 20 +++++++++++ src/models/response_data.rs | 3 +- src/routes/bestiary.rs | 33 ++++++++++++------- src/services/bestiary_service.rs | 14 ++++---- 10 files changed, 142 insertions(+), 34 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8b9623a..a09ca87 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Stage 1: Build the Rust project -FROM rust:1-alpine as builder +FROM rust:1-alpine AS builder # Set the working directory in the container WORKDIR /app diff --git a/src/db/bestiary_proxy.rs b/src/db/bestiary_proxy.rs index 080fbe9..3f85eba 100644 --- a/src/db/bestiary_proxy.rs +++ b/src/db/bestiary_proxy.rs @@ -11,7 +11,7 @@ use crate::models::creature::creature_metadata::creature_role::CreatureRoleEnum; use crate::models::creature::creature_metadata::type_enum::CreatureTypeEnum; use crate::models::creature::creature_metadata::variant_enum::CreatureVariant; use crate::models::pf_version_enum::PathfinderVersionEnum; -use crate::models::response_data::OptionalData; +use crate::models::response_data::ResponseDataModifiers; use crate::models::routers_validator_structs::{CreatureFieldFilters, OrderEnum}; use crate::AppState; use anyhow::Result; @@ -22,25 +22,24 @@ pub async fn get_creature_by_id( app_state: &AppState, id: i64, variant: &CreatureVariant, - optional_data: &OptionalData, + response_data_mods: &ResponseDataModifiers, ) -> Option { - let creature = creature_fetcher::fetch_creature_by_id(&app_state.conn, optional_data, id) + creature_fetcher::fetch_creature_by_id(&app_state.conn, variant, response_data_mods, id) .await - .ok()?; - Some(creature.convert_creature_to_variant(variant)) + .ok() } pub async fn get_weak_creature_by_id( app_state: &AppState, id: i64, - optional_data: &OptionalData, + optional_data: &ResponseDataModifiers, ) -> Option { get_creature_by_id(app_state, id, &CreatureVariant::Weak, optional_data).await } pub async fn get_elite_creature_by_id( app_state: &AppState, id: i64, - optional_data: &OptionalData, + optional_data: &ResponseDataModifiers, ) -> Option { get_creature_by_id(app_state, id, &CreatureVariant::Elite, optional_data).await } diff --git a/src/db/data_providers/creature_fetcher.rs b/src/db/data_providers/creature_fetcher.rs index 592f22a..0008e23 100644 --- a/src/db/data_providers/creature_fetcher.rs +++ b/src/db/data_providers/creature_fetcher.rs @@ -29,7 +29,7 @@ use crate::models::item::armor_struct::Armor; use crate::models::item::item_struct::Item; use crate::models::item::shield_struct::Shield; use crate::models::item::weapon_struct::Weapon; -use crate::models::response_data::OptionalData; +use crate::models::response_data::ResponseDataModifiers; use crate::models::scales_struct::ability_scales::AbilityScales; use crate::models::scales_struct::ac_scales::AcScales; use crate::models::scales_struct::area_dmg_scales::AreaDmgScales; @@ -430,34 +430,41 @@ pub async fn fetch_traits_associated_with_creatures(conn: &Pool) -> Resu pub async fn fetch_creature_by_id( conn: &Pool, - optional_data: &OptionalData, + variant: &CreatureVariant, + response_data_mods: &ResponseDataModifiers, id: i64, ) -> Result { let core_data = fetch_creature_core_data(conn, id).await?; let level = core_data.essential.level; let archive_link = core_data.derived.archive_link.clone(); - Ok(Creature { + let cr = Creature { core_data, variant_data: CreatureVariantData { variant: CreatureVariant::Base, level, archive_link, }, - extra_data: if optional_data.extra_data.is_some_and(|x| x) { + extra_data: if response_data_mods.extra_data.is_some_and(|x| x) { Some(fetch_creature_extra_data(conn, id).await?) } else { None }, - combat_data: if optional_data.combat_data.is_some_and(|x| x) { + combat_data: if response_data_mods.combat_data.is_some_and(|x| x) { Some(fetch_creature_combat_data(conn, id).await?) } else { None }, - spell_caster_data: if optional_data.spell_casting_data.is_some_and(|x| x) { + spell_caster_data: if response_data_mods.spell_casting_data.is_some_and(|x| x) { Some(fetch_creature_spell_caster_data(conn, id).await?) } else { None }, + } + .convert_creature_to_variant(variant); + Ok(if response_data_mods.is_pwl_on.unwrap_or(false) { + cr + } else { + cr.convert_creature_to_pwl() }) } diff --git a/src/models/creature/creature_component/creature_combat.rs b/src/models/creature/creature_component/creature_combat.rs index 3783cc0..d1a546b 100644 --- a/src/models/creature/creature_component/creature_combat.rs +++ b/src/models/creature/creature_component/creature_combat.rs @@ -26,3 +26,29 @@ pub struct CreatureCombatData { pub saving_throws: SavingThrows, pub ac: i8, } + +impl CreatureCombatData { + fn add_mod_to_saving_throws_and_ac_and_wp_to_hit(self, modifier: i64) -> CreatureCombatData { + let mut com_data = self; + let weapons: Vec = com_data + .weapons + .into_iter() + .map(|mut wp| { + wp.weapon_data.to_hit_bonus = + wp.weapon_data.to_hit_bonus.map(|to_hit| to_hit - modifier); + wp + }) + .collect(); + com_data.ac = (com_data.ac as i64 - modifier) as i8; + com_data.saving_throws.fortitude -= modifier; + com_data.saving_throws.reflex -= modifier; + com_data.saving_throws.will -= modifier; + com_data.weapons = weapons; + com_data + } + + /// Lowers saving throws, weapon to hit bonus, and ac by the given pwl_mod + pub fn convert_from_base_to_pwl(self, pwl_mod: u64) -> CreatureCombatData { + self.add_mod_to_saving_throws_and_ac_and_wp_to_hit(-(pwl_mod as i64)) + } +} diff --git a/src/models/creature/creature_component/creature_extra.rs b/src/models/creature/creature_component/creature_extra.rs index aedcc27..0916dbe 100644 --- a/src/models/creature/creature_component/creature_extra.rs +++ b/src/models/creature/creature_component/creature_extra.rs @@ -30,3 +30,26 @@ pub struct CreatureExtraData { pub perception: i8, pub perception_detail: Option, } + +impl CreatureExtraData { + fn add_mod_to_perception_and_skill_mods(self, modifier: i64) -> CreatureExtraData { + let mut ex_data = self; + // we should never have a pwl much greater than perception (pwl=lvl) + ex_data.perception = (ex_data.perception as i64 + modifier) as i8; + + ex_data.skills = ex_data + .skills + .into_iter() + .map(|mut skill| { + skill.modifier += modifier; + skill + }) + .collect(); + + ex_data + } + /// Lowers skill and perception by the given pwl_mod + pub fn convert_from_base_to_pwl(self, pwl_mod: u64) -> CreatureExtraData { + self.add_mod_to_perception_and_skill_mods(-(pwl_mod as i64)) + } +} diff --git a/src/models/creature/creature_component/creature_spell_caster.rs b/src/models/creature/creature_component/creature_spell_caster.rs index 75eec30..db4bb67 100644 --- a/src/models/creature/creature_component/creature_spell_caster.rs +++ b/src/models/creature/creature_component/creature_spell_caster.rs @@ -8,3 +8,26 @@ pub struct CreatureSpellCasterData { pub spells: Vec, pub spell_caster_entry: SpellCasterEntry, } + +impl CreatureSpellCasterData { + pub fn add_mod_to_spellcaster_atk_and_dc(self, pwl_mod: i64) -> CreatureSpellCasterData { + let mut spell_data = self; + + spell_data.spell_caster_entry.spell_casting_atk_mod = spell_data + .spell_caster_entry + .spell_casting_atk_mod + .map(|x| x - pwl_mod); + + spell_data.spell_caster_entry.spell_casting_dc_mod = spell_data + .spell_caster_entry + .spell_casting_dc_mod + .map(|x| x - pwl_mod); + + spell_data.clone() + } + + /// Lowers spell caster atk and dc + pub fn convert_from_base_to_pwl(self, pwl_mod: u64) -> CreatureSpellCasterData { + self.add_mod_to_spellcaster_atk_and_dc(-(pwl_mod as i64)) + } +} diff --git a/src/models/creature/creature_struct.rs b/src/models/creature/creature_struct.rs index 81ae191..f664e4a 100644 --- a/src/models/creature/creature_struct.rs +++ b/src/models/creature/creature_struct.rs @@ -26,6 +26,26 @@ impl Creature { cr.spell_caster_data = self.spell_caster_data; cr } + + pub fn convert_creature_to_pwl(self) -> Creature { + let pwl_mod = if self.core_data.essential.level >= 0 { + self.core_data.essential.level as u64 + } else { + 0 + }; + + Creature { + core_data: self.core_data, + variant_data: self.variant_data, + extra_data: self.extra_data.map(|x| x.convert_from_base_to_pwl(pwl_mod)), + combat_data: self + .combat_data + .map(|x| x.convert_from_base_to_pwl(pwl_mod)), + spell_caster_data: self + .spell_caster_data + .map(|x| x.convert_from_base_to_pwl(pwl_mod)), + } + } pub fn from_core(core: CreatureCoreData) -> Creature { let level = core.essential.level; let archive_link = core.derived.archive_link.clone(); diff --git a/src/models/response_data.rs b/src/models/response_data.rs index 4f7c744..6341b07 100644 --- a/src/models/response_data.rs +++ b/src/models/response_data.rs @@ -13,7 +13,8 @@ use utoipa::{IntoParams, ToSchema}; use validator::Validate; #[derive(Serialize, Deserialize, IntoParams, Default, Eq, PartialEq, Hash, Clone, Validate)] -pub struct OptionalData { +pub struct ResponseDataModifiers { + pub is_pwl_on: Option, pub extra_data: Option, pub combat_data: Option, pub spell_casting_data: Option, diff --git a/src/routes/bestiary.rs b/src/routes/bestiary.rs index eba5e18..a269816 100644 --- a/src/routes/bestiary.rs +++ b/src/routes/bestiary.rs @@ -2,8 +2,8 @@ use crate::models::creature::creature_metadata::alignment_enum::AlignmentEnum; use crate::models::creature::creature_metadata::creature_role::CreatureRoleEnum; use crate::models::creature::creature_metadata::type_enum::CreatureTypeEnum; use crate::models::creature::creature_metadata::variant_enum::CreatureVariant; -use crate::models::response_data::OptionalData; use crate::models::response_data::ResponseCreature; +use crate::models::response_data::ResponseDataModifiers; use crate::models::routers_validator_structs::OrderEnum; use crate::models::shared::rarity_enum::RarityEnum; use crate::models::shared::size_enum::SizeEnum; @@ -283,7 +283,7 @@ pub async fn get_creature_roles_list() -> Result { tag = "bestiary", params( ("creature_id" = String, Path, description = "id of the creature to fetch"), - OptionalData, + ResponseDataModifiers, ), responses( (status=200, description = "Successful Response", body = ResponseCreature), @@ -294,10 +294,11 @@ pub async fn get_creature_roles_list() -> Result { pub async fn get_creature( data: web::Data, creature_id: web::Path, - optional_data: Query, + response_data_mods: Query, ) -> Result { Ok(web::Json( - bestiary_service::get_creature(&data, sanitize_id(&creature_id)?, &optional_data.0).await, + bestiary_service::get_creature(&data, sanitize_id(&creature_id)?, &response_data_mods.0) + .await, )) } @@ -307,7 +308,7 @@ pub async fn get_creature( tag = "bestiary", params( ("creature_id" = String, Path, description = "id of the creature to fetch"), - OptionalData + ResponseDataModifiers ), responses( (status=200, description = "Successful Response", body = ResponseCreature), @@ -318,11 +319,15 @@ pub async fn get_creature( pub async fn get_elite_creature( data: web::Data, creature_id: web::Path, - response_data: Query, + response_data_mods: Query, ) -> Result { Ok(web::Json( - bestiary_service::get_elite_creature(&data, sanitize_id(&creature_id)?, &response_data.0) - .await, + bestiary_service::get_elite_creature( + &data, + sanitize_id(&creature_id)?, + &response_data_mods.0, + ) + .await, )) } @@ -332,7 +337,7 @@ pub async fn get_elite_creature( tag = "bestiary", params( ("creature_id" = String, Path, description = "id of the creature to fetch"), - OptionalData, + ResponseDataModifiers, ), responses( (status=200, description = "Successful Response", body = ResponseCreature), @@ -343,11 +348,15 @@ pub async fn get_elite_creature( pub async fn get_weak_creature( data: web::Data, creature_id: web::Path, - response_data: Query, + response_data_mods: Query, ) -> Result { Ok(web::Json( - bestiary_service::get_weak_creature(&data, sanitize_id(&creature_id)?, &response_data.0) - .await, + bestiary_service::get_weak_creature( + &data, + sanitize_id(&creature_id)?, + &response_data_mods.0, + ) + .await, )) } diff --git a/src/services/bestiary_service.rs b/src/services/bestiary_service.rs index 7f9fe6b..6cb343c 100644 --- a/src/services/bestiary_service.rs +++ b/src/services/bestiary_service.rs @@ -4,7 +4,7 @@ use crate::models::creature::creature_filter_enum::CreatureFilter; use crate::models::creature::creature_metadata::creature_role::CreatureRoleEnum; use crate::models::creature::creature_metadata::variant_enum::CreatureVariant; use crate::models::creature::creature_struct::Creature; -use crate::models::response_data::{OptionalData, ResponseCreature}; +use crate::models::response_data::{ResponseCreature, ResponseDataModifiers}; use crate::models::routers_validator_structs::CreatureFieldFilters; use crate::services::url_calculator::bestiary_next_url_calculator; use crate::AppState; @@ -24,33 +24,33 @@ pub struct BestiaryResponse { pub async fn get_creature( app_state: &AppState, id: i64, - optional_data: &OptionalData, + response_data_mods: &ResponseDataModifiers, ) -> HashMap> { hashmap! { String::from("results") => - bestiary_proxy::get_creature_by_id(app_state, id, &CreatureVariant::Base, optional_data).await.map(ResponseCreature::from) + bestiary_proxy::get_creature_by_id(app_state, id, &CreatureVariant::Base, response_data_mods).await.map(ResponseCreature::from) } } pub async fn get_elite_creature( app_state: &AppState, id: i64, - optional_data: &OptionalData, + response_data_mods: &ResponseDataModifiers, ) -> HashMap> { hashmap! { String::from("results") => - bestiary_proxy::get_elite_creature_by_id(app_state, id, optional_data).await.map(ResponseCreature::from) + bestiary_proxy::get_elite_creature_by_id(app_state, id, response_data_mods).await.map(ResponseCreature::from) } } pub async fn get_weak_creature( app_state: &AppState, id: i64, - optional_data: &OptionalData, + response_data_mods: &ResponseDataModifiers, ) -> HashMap> { hashmap! { String::from("results") => - bestiary_proxy::get_weak_creature_by_id(app_state, id, optional_data).await.map(ResponseCreature::from) + bestiary_proxy::get_weak_creature_by_id(app_state, id, response_data_mods).await.map(ResponseCreature::from) } }