This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
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)),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user