Close DDD cleanup and tests-support closure
This commit is contained in:
@@ -1,3 +1,170 @@
|
||||
//! 战斗写入命令过渡落位。
|
||||
//! 战斗写入命令。
|
||||
//!
|
||||
//! 用于表达创建战斗、使用技能、逃离和结算等输入,不直接携带表行类型。
|
||||
|
||||
use crate::domain::{BattleMode, LEGACY_ATTACK_FUNCTION_IDS};
|
||||
use crate::errors::CombatFieldError;
|
||||
use module_runtime_item::{
|
||||
RuntimeItemRewardItemSnapshot, TreasureFieldError, normalize_reward_item_snapshot,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use shared_kernel::normalize_required_string;
|
||||
#[cfg(feature = "spacetime-types")]
|
||||
use spacetimedb::SpacetimeType;
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BattleStateInput {
|
||||
pub battle_state_id: String,
|
||||
pub story_session_id: String,
|
||||
pub runtime_session_id: String,
|
||||
pub actor_user_id: String,
|
||||
pub chapter_id: Option<String>,
|
||||
pub target_npc_id: String,
|
||||
pub target_name: String,
|
||||
pub battle_mode: BattleMode,
|
||||
pub player_hp: i32,
|
||||
pub player_max_hp: i32,
|
||||
pub player_mana: i32,
|
||||
pub player_max_mana: i32,
|
||||
pub target_hp: i32,
|
||||
pub target_max_hp: i32,
|
||||
pub experience_reward: u32,
|
||||
pub reward_items: Vec<RuntimeItemRewardItemSnapshot>,
|
||||
pub created_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ResolveCombatActionInput {
|
||||
pub battle_state_id: String,
|
||||
pub function_id: String,
|
||||
pub action_text: String,
|
||||
pub base_damage: i32,
|
||||
pub mana_cost: i32,
|
||||
pub heal: i32,
|
||||
pub mana_restore: i32,
|
||||
pub counter_multiplier_basis_points: u32,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BattleStateQueryInput {
|
||||
pub battle_state_id: String,
|
||||
}
|
||||
|
||||
pub fn validate_battle_state_input(input: &BattleStateInput) -> Result<(), CombatFieldError> {
|
||||
if normalize_required_string(&input.battle_state_id).is_none() {
|
||||
return Err(CombatFieldError::MissingBattleStateId);
|
||||
}
|
||||
if normalize_required_string(&input.story_session_id).is_none() {
|
||||
return Err(CombatFieldError::MissingStorySessionId);
|
||||
}
|
||||
if normalize_required_string(&input.runtime_session_id).is_none() {
|
||||
return Err(CombatFieldError::MissingRuntimeSessionId);
|
||||
}
|
||||
if normalize_required_string(&input.actor_user_id).is_none() {
|
||||
return Err(CombatFieldError::MissingActorUserId);
|
||||
}
|
||||
if normalize_required_string(&input.target_npc_id).is_none() {
|
||||
return Err(CombatFieldError::MissingTargetNpcId);
|
||||
}
|
||||
if normalize_required_string(&input.target_name).is_none() {
|
||||
return Err(CombatFieldError::MissingTargetName);
|
||||
}
|
||||
if input.player_max_hp <= 0 || input.player_hp <= 0 || input.player_hp > input.player_max_hp {
|
||||
return Err(CombatFieldError::InvalidPlayerVitals);
|
||||
}
|
||||
if input.player_max_mana < 0
|
||||
|| input.player_mana < 0
|
||||
|| input.player_mana > input.player_max_mana
|
||||
{
|
||||
return Err(CombatFieldError::InvalidPlayerVitals);
|
||||
}
|
||||
if input.target_max_hp <= 0 || input.target_hp <= 0 || input.target_hp > input.target_max_hp {
|
||||
return Err(CombatFieldError::InvalidTargetVitals);
|
||||
}
|
||||
for reward_item in input.reward_items.iter().cloned() {
|
||||
normalize_reward_item_snapshot(reward_item).map_err(map_reward_item_field_error)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_resolve_combat_action_input(
|
||||
input: &ResolveCombatActionInput,
|
||||
) -> Result<(), CombatFieldError> {
|
||||
if normalize_required_string(&input.battle_state_id).is_none() {
|
||||
return Err(CombatFieldError::MissingBattleStateId);
|
||||
}
|
||||
if normalize_required_string(&input.function_id).is_none() {
|
||||
return Err(CombatFieldError::MissingFunctionId);
|
||||
}
|
||||
if !is_supported_combat_function_id(&input.function_id) {
|
||||
return Err(CombatFieldError::UnsupportedFunctionId);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn build_battle_state_query_input(
|
||||
battle_state_id: String,
|
||||
) -> Result<BattleStateQueryInput, CombatFieldError> {
|
||||
let input = BattleStateQueryInput {
|
||||
battle_state_id: normalize_required_string(battle_state_id).unwrap_or_default(),
|
||||
};
|
||||
|
||||
validate_battle_state_query_input(&input)?;
|
||||
|
||||
Ok(input)
|
||||
}
|
||||
|
||||
pub fn validate_battle_state_query_input(
|
||||
input: &BattleStateQueryInput,
|
||||
) -> Result<(), CombatFieldError> {
|
||||
if normalize_required_string(&input.battle_state_id).is_none() {
|
||||
return Err(CombatFieldError::MissingBattleStateId);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_supported_combat_function_id(function_id: &str) -> bool {
|
||||
matches!(
|
||||
function_id,
|
||||
"battle_attack_basic"
|
||||
| "battle_recover_breath"
|
||||
| "battle_use_skill"
|
||||
| "battle_escape_breakout"
|
||||
) || LEGACY_ATTACK_FUNCTION_IDS.contains(&function_id)
|
||||
}
|
||||
|
||||
fn map_reward_item_field_error(error: TreasureFieldError) -> CombatFieldError {
|
||||
let message = match error {
|
||||
TreasureFieldError::MissingRewardItemId => {
|
||||
"battle_state.reward_items[].item_id 不能为空".to_string()
|
||||
}
|
||||
TreasureFieldError::MissingRewardItemCategory => {
|
||||
"battle_state.reward_items[].category 不能为空".to_string()
|
||||
}
|
||||
TreasureFieldError::MissingRewardItemName => {
|
||||
"battle_state.reward_items[].item_name 不能为空".to_string()
|
||||
}
|
||||
TreasureFieldError::InvalidRewardItemQuantity => {
|
||||
"battle_state.reward_items[].quantity 必须大于 0".to_string()
|
||||
}
|
||||
TreasureFieldError::MissingRewardItemStackKey => {
|
||||
"battle_state.reward_items[].stack_key 不能为空".to_string()
|
||||
}
|
||||
TreasureFieldError::RewardEquipmentItemCannotStack => {
|
||||
"battle_state.reward_items[] 可装备物品不能标记为 stackable".to_string()
|
||||
}
|
||||
TreasureFieldError::RewardNonStackableItemMustStaySingleQuantity => {
|
||||
"battle_state.reward_items[] 不可堆叠物品必须固定为单槽位单数量".to_string()
|
||||
}
|
||||
other => other.to_string(),
|
||||
};
|
||||
|
||||
CombatFieldError::InvalidRewardItem(message)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user