Skip to content

Commit

Permalink
feat: Add in creature endpoint PWL and refactor creature variant, min…
Browse files Browse the repository at this point in the history
…or fixes (#62)

* add pwl for single creature fetch (base,weak,elite)
* update variant logic to modify more fields
* wrong hp level in variant logic
* modify core level for variant
* get avg now is implemented in dice and can handle a weapon with multiple damage sources
* add item sources endpoint, filters and sort
* change shop list from get to post, add request body, allow multiple filters for shop
  • Loading branch information
RakuJa authored Jul 22, 2024
1 parent 0e02d27 commit e0d3727
Show file tree
Hide file tree
Showing 26 changed files with 645 additions and 311 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ counter = "0.6.0"
ordered-float = { version = "4", features = ["serde"]}
num-traits = "0.2.19"
maplit = "1.0.2"
itertools = "0.13.0"

regex = "1.10.5"

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -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
Expand Down
23 changes: 11 additions & 12 deletions build/creature_core_db_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,18 @@ async fn create_temporary_table(conn: &Pool<Sqlite>) -> Result<()> {
ct.source,
ct.remaster,
CASE WHEN ct.id IN (
SELECT creature_id FROM (
SELECT wcat.creature_id, base_item_id FROM ITEM_CREATURE_ASSOCIATION_TABLE wcat LEFT JOIN (
SELECT * FROM WEAPON_TABLE w1 WHERE UPPER(w1.weapon_type) = 'MELEE'
) wt ON base_item_id = wcat.item_id
)
SELECT wcat.creature_id
FROM WEAPON_CREATURE_ASSOCIATION_TABLE wcat LEFT JOIN (
SELECT * FROM WEAPON_TABLE w1 WHERE UPPER(w1.weapon_type) = 'MELEE'
) wt ON base_item_id = wcat.weapon_id
) THEN TRUE ELSE FALSE END AS is_melee,
CASE WHEN ct.id IN (
SELECT creature_id FROM (
SELECT wcat.creature_id, base_item_id FROM ITEM_CREATURE_ASSOCIATION_TABLE wcat LEFT JOIN (
SELECT * FROM WEAPON_TABLE w1 WHERE UPPER(w1.weapon_type) = 'RANGED'
) wt ON base_item_id = wcat.item_id
)
) THEN TRUE ELSE FALSE END AS is_ranged,
CASE WHEN ct.id IN (
SELECT wcat.creature_id
FROM WEAPON_CREATURE_ASSOCIATION_TABLE wcat LEFT JOIN (
SELECT * FROM WEAPON_TABLE w1 WHERE UPPER(w1.weapon_type) = 'MELEE'
) wt ON base_item_id = wcat.weapon_id
)
THEN TRUE ELSE FALSE END AS is_ranged,
CASE WHEN st.creature_id IS NOT NULL THEN TRUE ELSE FALSE END AS is_spell_caster,
CASE WHEN ct.aon_id IS NOT NULL THEN CONCAT('https://2e.aonprd.com/', CAST(UPPER(COALESCE(UPPER(ct.cr_type) , 'MONSTER')) AS TEXT), 's' , '.aspx?ID=', CAST(ct.aon_id AS TEXT)) ELSE NULL END AS archive_link,
COALESCE(ct.cr_type , 'Monster') AS cr_type,
Expand Down
25 changes: 12 additions & 13 deletions src/db/bestiary_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -21,28 +21,27 @@ use strum::IntoEnumIterator;
pub async fn get_creature_by_id(
app_state: &AppState,
id: i64,
variant: &CreatureVariant,
optional_data: &OptionalData,
variant: CreatureVariant,
response_data_mods: &ResponseDataModifiers,
) -> Option<Creature> {
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<Creature> {
get_creature_by_id(app_state, id, &CreatureVariant::Weak, optional_data).await
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<Creature> {
get_creature_by_id(app_state, id, &CreatureVariant::Elite, optional_data).await
get_creature_by_id(app_state, id, CreatureVariant::Elite, optional_data).await
}

pub async fn get_paginated_creatures(
Expand Down Expand Up @@ -142,13 +141,13 @@ pub async fn get_creatures_passing_all_filters(
if fetch_weak && level_vec.contains(&(core.essential.level - 1).to_string()) {
creature_vec.push(Creature::from_core_with_variant(
core.clone(),
&CreatureVariant::Weak,
CreatureVariant::Weak,
));
}
if fetch_elite && level_vec.contains(&(core.essential.level + 1).to_string()) {
creature_vec.push(Creature::from_core_with_variant(
core.clone(),
&CreatureVariant::Elite,
CreatureVariant::Elite,
));
}
creature_vec.push(Creature::from_core(core));
Expand Down Expand Up @@ -241,7 +240,7 @@ async fn get_list(app_state: &AppState, variant: CreatureVariant) -> Vec<Creatur
CreatureVariant::Base => creatures.into_iter().map(Creature::from_core).collect(),
_ => creatures
.into_iter()
.map(|cr| Creature::from_core_with_variant(cr, &variant))
.map(|cr| Creature::from_core_with_variant(cr, variant.clone()))
.collect(),
};
}
Expand Down
124 changes: 94 additions & 30 deletions src/db/data_providers/creature_fetcher.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::db::data_providers::generic_fetcher::{
fetch_armor_runes, fetch_item_traits, fetch_weapon_runes, MyString,
fetch_armor_runes, fetch_armor_traits, fetch_item_traits, fetch_shield_traits,
fetch_weapon_damage_data, fetch_weapon_runes, fetch_weapon_traits, MyString,
};
use crate::db::data_providers::raw_query_builder::prepare_filtered_get_creatures_core;
use crate::models::creature::creature_component::creature_combat::{
Expand Down Expand Up @@ -29,7 +30,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;
Expand Down Expand Up @@ -210,15 +211,14 @@ pub async fn fetch_creature_traits(conn: &Pool<Sqlite>, creature_id: i64) -> Res
async fn fetch_creature_weapons(conn: &Pool<Sqlite>, creature_id: i64) -> Result<Vec<Weapon>> {
let weapons: Vec<Weapon> = sqlx::query_as(
"
SELECT wt.id AS weapon_id, wt.bonus_dmg, wt.to_hit_bonus, wt.dmg_type,
wt.number_of_dice, wt.die_size, wt.splash_dmg, wt.n_of_potency_runes,
SELECT wt.id AS weapon_id, wt.to_hit_bonus, wt.splash_dmg, wt.n_of_potency_runes,
wt.n_of_striking_runes, wt.range, wt.reload, wt.weapon_type, wt.base_item_id,
it.*
FROM WEAPON_TABLE wt LEFT JOIN ITEM_TABLE it ON it.id = wt.base_item_id
WHERE base_item_id IN (
SELECT item_id FROM ITEM_CREATURE_ASSOCIATION_TABLE WHERE creature_id == ($1)
)
GROUP BY it.id
FROM WEAPON_CREATURE_ASSOCIATION_TABLE ica
LEFT JOIN WEAPON_TABLE wt ON wt.id = ica.weapon_id
LEFT JOIN ITEM_TABLE it ON it.id = wt.base_item_id
WHERE ica.creature_id = ($1)
GROUP BY ica.weapon_id
ORDER BY name
",
)
Expand All @@ -227,13 +227,16 @@ async fn fetch_creature_weapons(conn: &Pool<Sqlite>, creature_id: i64) -> Result
.await?;
let mut result_vec = Vec::new();
for mut el in weapons {
el.item_core.traits = fetch_item_traits(conn, el.item_core.id)
el.item_core.traits = fetch_weapon_traits(conn, el.weapon_data.id)
.await
.unwrap_or(vec![]);
el.item_core.quantity = fetch_item_quantity(conn, creature_id, el.item_core.id).await;
el.item_core.quantity = fetch_weapon_quantity(conn, creature_id, el.weapon_data.id).await;
el.weapon_data.property_runes = fetch_weapon_runes(conn, el.weapon_data.id)
.await
.unwrap_or(vec![]);
el.weapon_data.damage_data = fetch_weapon_damage_data(conn, el.weapon_data.id)
.await
.unwrap_or(vec![]);
result_vec.push(el)
}
Ok(result_vec)
Expand All @@ -245,11 +248,11 @@ async fn fetch_creature_armors(conn: &Pool<Sqlite>, creature_id: i64) -> Result<
SELECT at.id AS armor_id, at.bonus_ac, at.check_penalty, at.dex_cap, at.n_of_potency_runes,
at.n_of_resilient_runes, at.speed_penalty, at.strength_required, at.base_item_id,
it.*
FROM ARMOR_TABLE at LEFT JOIN ITEM_TABLE it ON it.id = at.base_item_id
WHERE base_item_id IN (
SELECT item_id FROM ITEM_CREATURE_ASSOCIATION_TABLE WHERE creature_id == ($1)
)
GROUP BY it.id
FROM ARMOR_CREATURE_ASSOCIATION_TABLE aca
LEFT JOIN ARMOR_TABLE at ON at.id = aca.armor_id
LEFT JOIN ITEM_TABLE it ON it.id = at.base_item_id
WHERE aca.creature_id = ($1)
GROUP BY aca.armor_id
ORDER BY name
",
)
Expand All @@ -258,10 +261,10 @@ async fn fetch_creature_armors(conn: &Pool<Sqlite>, creature_id: i64) -> Result<
.await?;
let mut result_vec = Vec::new();
for mut el in armors {
el.item_core.traits = fetch_item_traits(conn, el.item_core.id)
el.item_core.traits = fetch_armor_traits(conn, el.armor_data.id)
.await
.unwrap_or(vec![]);
el.item_core.quantity = fetch_item_quantity(conn, creature_id, el.item_core.id).await;
el.item_core.quantity = fetch_armor_quantity(conn, creature_id, el.armor_data.id).await;
el.armor_data.property_runes = fetch_armor_runes(conn, el.armor_data.id)
.await
.unwrap_or(vec![]);
Expand All @@ -275,11 +278,11 @@ async fn fetch_creature_shields(conn: &Pool<Sqlite>, creature_id: i64) -> Result
"
SELECT st.id AS shield_id, st.bonus_ac, st.n_of_reinforcing_runes, st.speed_penalty,
it.*
FROM SHIELD_TABLE st LEFT JOIN ITEM_TABLE it ON it.id = st.base_item_id
WHERE base_item_id IN (
SELECT item_id FROM ITEM_CREATURE_ASSOCIATION_TABLE WHERE creature_id == ($1)
)
GROUP BY it.id
FROM SHIELD_CREATURE_ASSOCIATION_TABLE sca
LEFT JOIN SHIELD_TABLE st ON st.id = sca.shield_id
LEFT JOIN ITEM_TABLE it ON it.id = st.base_item_id
WHERE sca.creature_id = ($1)
GROUP BY sca.shield_id
ORDER BY name
",
)
Expand All @@ -288,10 +291,10 @@ async fn fetch_creature_shields(conn: &Pool<Sqlite>, creature_id: i64) -> Result
.await?;
let mut result_vec = Vec::new();
for mut el in shields {
el.item_core.traits = fetch_item_traits(conn, el.item_core.id)
el.item_core.traits = fetch_shield_traits(conn, el.shield_data.id)
.await
.unwrap_or(vec![]);
el.item_core.quantity = fetch_item_quantity(conn, creature_id, el.item_core.id).await;
el.item_core.quantity = fetch_shield_quantity(conn, creature_id, el.shield_data.id).await;
result_vec.push(el)
}
Ok(result_vec)
Expand Down Expand Up @@ -336,6 +339,60 @@ async fn fetch_item_quantity(conn: &Pool<Sqlite>, creature_id: i64, item_id: i64
}
}

/// Quantities are present ONLY for creature's weapons.
/// It needs to be fetched from the association table.
/// It defaults to 1 if error are found
async fn fetch_weapon_quantity(conn: &Pool<Sqlite>, creature_id: i64, weapon_id: i64) -> i64 {
match sqlx::query!(
"SELECT quantity FROM WEAPON_CREATURE_ASSOCIATION_TABLE WHERE
creature_id == ($1) AND weapon_id == ($2)",
creature_id,
weapon_id
)
.fetch_one(conn)
.await
{
Ok(r) => r.quantity,
Err(_) => 1,
}
}

/// Quantities are present ONLY for creature's shields.
/// It needs to be fetched from the association table.
/// It defaults to 1 if error are found
async fn fetch_shield_quantity(conn: &Pool<Sqlite>, creature_id: i64, shield_id: i64) -> i64 {
match sqlx::query!(
"SELECT quantity FROM SHIELD_CREATURE_ASSOCIATION_TABLE WHERE
creature_id == ($1) AND shield_id == ($2)",
creature_id,
shield_id
)
.fetch_one(conn)
.await
{
Ok(r) => r.quantity,
Err(_) => 1,
}
}

/// Quantities are present ONLY for creature's armors.
/// It needs to be fetched from the association table.
/// It defaults to 1 if error are found
async fn fetch_armor_quantity(conn: &Pool<Sqlite>, creature_id: i64, armor_id: i64) -> i64 {
match sqlx::query!(
"SELECT quantity FROM ARMOR_CREATURE_ASSOCIATION_TABLE WHERE
creature_id == ($1) AND armor_id == ($2)",
creature_id,
armor_id
)
.fetch_one(conn)
.await
{
Ok(r) => r.quantity,
Err(_) => 1,
}
}

async fn fetch_creature_actions(conn: &Pool<Sqlite>, creature_id: i64) -> Result<Vec<Action>> {
Ok(sqlx::query_as!(
Action,
Expand Down Expand Up @@ -430,34 +487,41 @@ pub async fn fetch_traits_associated_with_creatures(conn: &Pool<Sqlite>) -> Resu

pub async fn fetch_creature_by_id(
conn: &Pool<Sqlite>,
optional_data: &OptionalData,
variant: CreatureVariant,
response_data_mods: &ResponseDataModifiers,
id: i64,
) -> Result<Creature> {
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.convert_creature_to_pwl()
} else {
cr
})
}

Expand Down
Loading

0 comments on commit e0d3727

Please sign in to comment.