sb-api: label helpers wiring + gym/stat_value + policy constants (WI-2, issue #2)
This commit is contained in:
parent
5d5a594a7a
commit
f0659421e7
7 changed files with 217 additions and 47 deletions
|
|
@ -4,7 +4,7 @@ use crate::middleware::auth::AuthUser;
|
|||
use axum::extract::{Path, Query, State};
|
||||
use axum::routing::{get, post};
|
||||
use axum::{Extension, Json, Router};
|
||||
use sb_core::character::{self, CLASSES, RACES, TRAINING_GROUNDS};
|
||||
use sb_core::character::{self, CLASSES, GYM_STAT_DISPLAY_ORDER, RACES, TRAINING_GROUNDS};
|
||||
use sb_core::data;
|
||||
use sb_core::items;
|
||||
use sb_core::services as domain;
|
||||
|
|
@ -23,6 +23,16 @@ use tokio::sync::RwLock;
|
|||
|
||||
mod responses;
|
||||
|
||||
const DEFAULT_ZONE_ID: &str = "skara_brae_streets";
|
||||
const CHARACTER_NAME_MAX_LEN: usize = 32;
|
||||
const REGISTER_NAME_MIN_LEN: usize = 3;
|
||||
const REGISTER_NAME_MAX_LEN: usize = 32;
|
||||
const PASSWORD_MIN_LEN: usize = 6;
|
||||
const PARTIAL_HEAL_FRACTION: f64 = 0.5;
|
||||
const MIN_HEAL_COST: i32 = 1;
|
||||
const SPELL_LEARN_COST_DISPLAY_MIN: i32 = 2;
|
||||
const SPELL_LEARN_COST_DISPLAY_MAX: i32 = 7;
|
||||
|
||||
fn request_rng() -> rand::rngs::StdRng {
|
||||
use rand::SeedableRng;
|
||||
rand::rngs::StdRng::from_os_rng()
|
||||
|
|
@ -314,7 +324,7 @@ async fn create_character(
|
|||
}
|
||||
|
||||
let name = input.name.trim().to_string();
|
||||
if name.is_empty() || name.len() > 32 {
|
||||
if name.is_empty() || name.len() > CHARACTER_NAME_MAX_LEN {
|
||||
return Err(axum::http::StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
|
@ -450,7 +460,7 @@ async fn run_adventure(
|
|||
Path(char_id): Path<u32>,
|
||||
Query(query): Query<AdventureRequest>,
|
||||
) -> Result<Json<responses::AdventureRunResponse>, axum::http::StatusCode> {
|
||||
let zone_id = query.zone_id.as_deref().unwrap_or("skara_brae_streets");
|
||||
let zone_id = query.zone_id.as_deref().unwrap_or(DEFAULT_ZONE_ID);
|
||||
|
||||
{
|
||||
let chars = state.characters.read().await;
|
||||
|
|
@ -521,27 +531,18 @@ async fn get_gym(
|
|||
.ok_or(axum::http::StatusCode::NOT_FOUND)?
|
||||
.clone()
|
||||
};
|
||||
let stats = [
|
||||
("strength", char.stats.strength),
|
||||
("intelligence", char.stats.intelligence),
|
||||
("dexterity", char.stats.dexterity),
|
||||
("constitution", char.stats.constitution),
|
||||
("luck", char.stats.luck),
|
||||
];
|
||||
let stat_responses: Vec<TrainingStatResponse> = stats
|
||||
let stat_responses: Vec<TrainingStatResponse> = GYM_STAT_DISPLAY_ORDER
|
||||
.iter()
|
||||
.map(|(name, val)| {
|
||||
let ground = TRAINING_GROUNDS
|
||||
.iter()
|
||||
.find(|g| g.stat == *name)
|
||||
.expect("valid stat");
|
||||
TrainingStatResponse {
|
||||
stat: name.to_string(),
|
||||
.filter_map(|stat_name| {
|
||||
let ground = TRAINING_GROUNDS.iter().find(|g| g.stat == *stat_name)?;
|
||||
let value = character::stat_value(&char.stats, stat_name)?;
|
||||
Some(TrainingStatResponse {
|
||||
stat: stat_name.to_string(),
|
||||
name: ground.name.to_string(),
|
||||
description: ground.description.to_string(),
|
||||
value: *val,
|
||||
cost: character::training_cost(*val),
|
||||
}
|
||||
value,
|
||||
cost: character::training_cost(value),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
Ok(Json(GymResponse {
|
||||
|
|
@ -630,16 +631,8 @@ async fn get_shop(
|
|||
.map(|item| ShopItemResponse {
|
||||
id: item.id.clone(),
|
||||
name: item.name.clone(),
|
||||
item_type: serde_json::to_value(&item.r#type)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
quality: serde_json::to_value(&item.quality)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
item_type: item.r#type.as_str().to_string(),
|
||||
quality: item.quality.as_str().to_string(),
|
||||
slot: Some(item.slot.clone()),
|
||||
stats: item.stats.clone(),
|
||||
description: item.description.clone(),
|
||||
|
|
@ -659,16 +652,8 @@ async fn get_shop(
|
|||
.map(|item| InventoryItemResponse {
|
||||
id: item.id.clone(),
|
||||
name: item.name.clone(),
|
||||
item_type: serde_json::to_value(&item.r#type)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
quality: serde_json::to_value(&item.quality)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
item_type: item.r#type.as_str().to_string(),
|
||||
quality: item.quality.as_str().to_string(),
|
||||
slot: Some(item.slot.clone()),
|
||||
stats: item.stats.clone(),
|
||||
description: item.description.clone(),
|
||||
|
|
@ -859,11 +844,11 @@ async fn heal_character(
|
|||
|
||||
let heal_amount = match input.amount.as_deref() {
|
||||
Some("full") => missing,
|
||||
Some("partial") => (missing as f64 / 2.0).ceil() as i32,
|
||||
Some("partial") => (missing as f64 * PARTIAL_HEAL_FRACTION).ceil() as i32,
|
||||
_ => missing,
|
||||
};
|
||||
let actual = heal_amount.min(missing);
|
||||
let cost = actual.max(1);
|
||||
let cost = actual.max(MIN_HEAL_COST);
|
||||
|
||||
if char.gold < cost {
|
||||
return Err(axum::http::StatusCode::UNPROCESSABLE_ENTITY);
|
||||
|
|
@ -1039,7 +1024,7 @@ async fn get_spells(
|
|||
spell_points: s.spell_points,
|
||||
})
|
||||
.collect();
|
||||
let learn_costs = (2..=7)
|
||||
let learn_costs = (SPELL_LEARN_COST_DISPLAY_MIN..=SPELL_LEARN_COST_DISPLAY_MAX)
|
||||
.map(|level| (level.to_string(), spells::spell_learn_cost(level)))
|
||||
.collect::<BTreeMap<String, i32>>();
|
||||
Ok(Json(responses::SpellbookResponse {
|
||||
|
|
@ -1156,7 +1141,9 @@ async fn learn_spell_level(
|
|||
return Err(axum::http::StatusCode::FORBIDDEN);
|
||||
}
|
||||
|
||||
if !(1..=7).contains(&spell_level) {
|
||||
if !(sb_core::constants::MIN_SPELL_LEVEL..=sb_core::constants::MAX_SPELL_LEVEL)
|
||||
.contains(&spell_level)
|
||||
{
|
||||
return Err(axum::http::StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
|
@ -1228,10 +1215,10 @@ async fn register(
|
|||
State(state): State<Arc<AppState>>,
|
||||
Json(input): Json<RegisterRequest>,
|
||||
) -> Result<Json<AuthResponse>, axum::http::StatusCode> {
|
||||
if input.name.len() < 3 || input.name.len() > 32 {
|
||||
if input.name.len() < REGISTER_NAME_MIN_LEN || input.name.len() > REGISTER_NAME_MAX_LEN {
|
||||
return Err(axum::http::StatusCode::BAD_REQUEST);
|
||||
}
|
||||
if input.password.len() < 6 {
|
||||
if input.password.len() < PASSWORD_MIN_LEN {
|
||||
return Err(axum::http::StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue