221 lines
8.3 KiB
Rust
221 lines
8.3 KiB
Rust
use serde_json::Value;
|
|
|
|
use shared_contracts::runtime_story::RuntimeStoryActionRequest;
|
|
|
|
use crate::{
|
|
StoryResolution, add_inventory_items_to_list, build_current_build_toast, current_world_type,
|
|
ensure_inventory_action_available, find_player_inventory_entry, read_i32_field,
|
|
read_inventory_item_name, read_optional_string_field, read_player_inventory_values,
|
|
remove_inventory_item_from_list, resolve_action_text, resolve_equipment_slot_for_item,
|
|
write_i32_field, write_player_inventory_values,
|
|
};
|
|
|
|
use super::forge::{
|
|
apply_forge_requirements_if_possible, build_dismantle_outputs, build_forge_recipe_result_item,
|
|
build_forge_success_text, build_reforged_item, forge_recipe_definition, format_currency_text,
|
|
reforge_cost_definition,
|
|
};
|
|
|
|
/// 锻造动作编排已经不再依赖 `api-server` 的 HTTP 边界。
|
|
///
|
|
/// 这里继续沿用 compat 快照态结算,后续可直接被 `api-server` 外壳或真相态桥接层复用。
|
|
pub fn resolve_forge_craft_action(
|
|
game_state: &mut Value,
|
|
request: &RuntimeStoryActionRequest,
|
|
) -> Result<StoryResolution, String> {
|
|
ensure_inventory_action_available(
|
|
game_state,
|
|
"缺少玩家角色,无法执行锻造配方。",
|
|
"战斗中无法使用工坊。",
|
|
)?;
|
|
let recipe_id = request
|
|
.action
|
|
.payload
|
|
.as_ref()
|
|
.and_then(|payload| read_optional_string_field(payload, "recipeId"))
|
|
.or_else(|| request.action.target_id.clone())
|
|
.ok_or_else(|| "forge_craft 缺少 recipeId".to_string())?;
|
|
let recipe = forge_recipe_definition(recipe_id.as_str())
|
|
.ok_or_else(|| "未找到目标锻造配方。".to_string())?;
|
|
let player_currency = read_i32_field(game_state, "playerCurrency").unwrap_or(0);
|
|
if player_currency < recipe.currency_cost {
|
|
return Err(format!("{} 当前材料或货币不足。", recipe.name));
|
|
}
|
|
let current_inventory = read_player_inventory_values(game_state);
|
|
let consumed_inventory = apply_forge_requirements_if_possible(
|
|
current_inventory.as_slice(),
|
|
recipe.requirements.as_slice(),
|
|
)
|
|
.ok_or_else(|| format!("{} 当前材料或货币不足。", recipe.name))?;
|
|
let created_item = build_forge_recipe_result_item(
|
|
game_state,
|
|
recipe.id,
|
|
current_world_type(game_state).as_deref(),
|
|
);
|
|
let next_inventory =
|
|
add_inventory_items_to_list(consumed_inventory, vec![created_item.clone()]);
|
|
|
|
write_i32_field(
|
|
game_state,
|
|
"playerCurrency",
|
|
player_currency.saturating_sub(recipe.currency_cost),
|
|
);
|
|
write_player_inventory_values(game_state, next_inventory);
|
|
|
|
Ok(StoryResolution {
|
|
action_text: resolve_action_text(
|
|
&format!("制作{}", read_inventory_item_name(&created_item)),
|
|
request,
|
|
),
|
|
result_text: build_forge_success_text(
|
|
"craft",
|
|
Some(recipe.name),
|
|
None,
|
|
Some(read_inventory_item_name(&created_item).as_str()),
|
|
&[],
|
|
Some(format_currency_text(
|
|
recipe.currency_cost,
|
|
current_world_type(game_state).as_deref(),
|
|
)),
|
|
),
|
|
story_text: None,
|
|
presentation_options: None,
|
|
saved_current_story: None,
|
|
patches: Vec::new(),
|
|
battle: None,
|
|
toast: Some(build_current_build_toast(game_state)),
|
|
})
|
|
}
|
|
|
|
pub fn resolve_forge_dismantle_action(
|
|
game_state: &mut Value,
|
|
request: &RuntimeStoryActionRequest,
|
|
) -> Result<StoryResolution, String> {
|
|
ensure_inventory_action_available(
|
|
game_state,
|
|
"缺少玩家角色,无法执行拆解。",
|
|
"战斗中无法执行拆解。",
|
|
)?;
|
|
let item_id = request
|
|
.action
|
|
.payload
|
|
.as_ref()
|
|
.and_then(|payload| read_optional_string_field(payload, "itemId"))
|
|
.or_else(|| request.action.target_id.clone())
|
|
.ok_or_else(|| "forge_dismantle 缺少 itemId".to_string())?;
|
|
let item = find_player_inventory_entry(game_state, item_id.as_str())
|
|
.cloned()
|
|
.ok_or_else(|| "未找到可拆解的物品。".to_string())?;
|
|
if read_i32_field(&item, "quantity").unwrap_or(0) <= 0 {
|
|
return Err("未找到可拆解的物品。".to_string());
|
|
}
|
|
let outputs = build_dismantle_outputs(game_state, &item)
|
|
.ok_or_else(|| format!("{} 当前不支持拆解。", read_inventory_item_name(&item)))?;
|
|
let mut next_inventory = read_player_inventory_values(game_state);
|
|
next_inventory = remove_inventory_item_from_list(next_inventory, item_id.as_str(), 1);
|
|
next_inventory = add_inventory_items_to_list(next_inventory, outputs.clone());
|
|
write_player_inventory_values(game_state, next_inventory);
|
|
let output_names = outputs
|
|
.iter()
|
|
.map(read_inventory_item_name)
|
|
.collect::<Vec<_>>();
|
|
|
|
Ok(StoryResolution {
|
|
action_text: resolve_action_text(
|
|
&format!("拆解{}", read_inventory_item_name(&item)),
|
|
request,
|
|
),
|
|
result_text: build_forge_success_text(
|
|
"dismantle",
|
|
None,
|
|
Some(read_inventory_item_name(&item).as_str()),
|
|
None,
|
|
output_names.as_slice(),
|
|
None,
|
|
),
|
|
story_text: None,
|
|
presentation_options: None,
|
|
saved_current_story: None,
|
|
patches: Vec::new(),
|
|
battle: None,
|
|
toast: Some(build_current_build_toast(game_state)),
|
|
})
|
|
}
|
|
|
|
pub fn resolve_forge_reforge_action(
|
|
game_state: &mut Value,
|
|
request: &RuntimeStoryActionRequest,
|
|
) -> Result<StoryResolution, String> {
|
|
ensure_inventory_action_available(
|
|
game_state,
|
|
"缺少玩家角色,无法执行重铸。",
|
|
"战斗中无法执行重铸。",
|
|
)?;
|
|
let item_id = request
|
|
.action
|
|
.payload
|
|
.as_ref()
|
|
.and_then(|payload| read_optional_string_field(payload, "itemId"))
|
|
.or_else(|| request.action.target_id.clone())
|
|
.ok_or_else(|| "forge_reforge 缺少 itemId".to_string())?;
|
|
let item = find_player_inventory_entry(game_state, item_id.as_str())
|
|
.cloned()
|
|
.ok_or_else(|| "未找到可重铸的物品。".to_string())?;
|
|
if read_i32_field(&item, "quantity").unwrap_or(0) <= 0 {
|
|
return Err("未找到可重铸的物品。".to_string());
|
|
}
|
|
let slot_id = resolve_equipment_slot_for_item(&item);
|
|
let reforge_cost = reforge_cost_definition(slot_id);
|
|
let player_currency = read_i32_field(game_state, "playerCurrency").unwrap_or(0);
|
|
if player_currency < reforge_cost.currency_cost {
|
|
return Err(format!(
|
|
"{} 当前不满足重铸条件。",
|
|
read_inventory_item_name(&item)
|
|
));
|
|
}
|
|
let reforged_item = build_reforged_item(game_state, &item)
|
|
.ok_or_else(|| format!("{} 当前不满足重铸条件。", read_inventory_item_name(&item)))?;
|
|
let base_inventory = remove_inventory_item_from_list(
|
|
read_player_inventory_values(game_state),
|
|
item_id.as_str(),
|
|
1,
|
|
);
|
|
let consumed_inventory = apply_forge_requirements_if_possible(
|
|
base_inventory.as_slice(),
|
|
reforge_cost.requirements.as_slice(),
|
|
)
|
|
.ok_or_else(|| format!("{} 当前不满足重铸条件。", read_inventory_item_name(&item)))?;
|
|
let next_inventory =
|
|
add_inventory_items_to_list(consumed_inventory, vec![reforged_item.clone()]);
|
|
write_player_inventory_values(game_state, next_inventory);
|
|
write_i32_field(
|
|
game_state,
|
|
"playerCurrency",
|
|
player_currency.saturating_sub(reforge_cost.currency_cost),
|
|
);
|
|
|
|
Ok(StoryResolution {
|
|
action_text: resolve_action_text(
|
|
&format!("重铸{}", read_inventory_item_name(&item)),
|
|
request,
|
|
),
|
|
result_text: build_forge_success_text(
|
|
"reforge",
|
|
None,
|
|
Some(read_inventory_item_name(&item).as_str()),
|
|
Some(read_inventory_item_name(&reforged_item).as_str()),
|
|
&[],
|
|
Some(format_currency_text(
|
|
reforge_cost.currency_cost,
|
|
current_world_type(game_state).as_deref(),
|
|
)),
|
|
),
|
|
story_text: None,
|
|
presentation_options: None,
|
|
saved_current_story: None,
|
|
patches: Vec::new(),
|
|
battle: None,
|
|
toast: Some(build_current_build_toast(game_state)),
|
|
})
|
|
}
|