fix: preserve rpg custom world detail profiles
This commit is contained in:
@@ -14,16 +14,18 @@ use crate::{
|
||||
build_current_build_toast, build_npc_gift_result_text,
|
||||
build_runtime_story_option_from_story_option, build_runtime_story_view_model,
|
||||
build_static_runtime_story_option, build_status_patch, build_story_option_from_runtime_option,
|
||||
clear_encounter_state, clone_inventory_item_with_quantity, current_encounter_name,
|
||||
ensure_json_object, find_player_inventory_entry, normalize_equipment_slot_id,
|
||||
normalize_required_string, npc_buyback_price, npc_purchase_price,
|
||||
clear_encounter_state, clear_post_battle_state, clone_inventory_item_with_quantity,
|
||||
current_encounter_name, ensure_json_object, ensure_scene_act_state,
|
||||
ensure_scene_encounter_preview, finalize_post_battle_resolution, find_player_inventory_entry,
|
||||
normalize_equipment_slot_id, normalize_required_string, npc_buyback_price, npc_purchase_price,
|
||||
project_story_engine_after_action, read_array_field, read_bool_field, read_field,
|
||||
read_i32_field, read_inventory_item_name, read_object_field, read_optional_string_field,
|
||||
read_player_equipment_item, read_player_inventory_values, read_runtime_session_id,
|
||||
read_u32_field, recruit_companion_to_party, remove_inventory_item_from_list,
|
||||
resolve_action_text, resolve_battle_action, resolve_equipment_slot_for_item,
|
||||
resolve_forge_craft_action, resolve_forge_dismantle_action, resolve_forge_reforge_action,
|
||||
resolve_npc_gift_affinity_gain, restore_player_resource, simple_story_resolution,
|
||||
resolve_forward_scene_id, resolve_npc_gift_affinity_gain, resolve_post_battle_story_options,
|
||||
resolve_runtime_scene_preset, restore_player_resource, simple_story_resolution,
|
||||
write_bool_field, write_i32_field, write_null_field, write_player_equipment_item,
|
||||
write_player_inventory_values, write_runtime_npc_interaction_view, write_string_field,
|
||||
write_u32_field,
|
||||
@@ -97,23 +99,7 @@ pub fn resolve_story_runtime_action(
|
||||
requested_runtime_session_id.as_str(),
|
||||
);
|
||||
|
||||
let mut options = resolution
|
||||
.presentation_options
|
||||
.take()
|
||||
.unwrap_or_else(|| build_fallback_runtime_story_options(&game_state));
|
||||
if options.is_empty() {
|
||||
options = build_fallback_runtime_story_options(&game_state);
|
||||
}
|
||||
|
||||
let story_text = resolution
|
||||
.story_text
|
||||
.clone()
|
||||
.unwrap_or_else(|| resolution.result_text.clone());
|
||||
let history_result_text = resolution.result_text.clone();
|
||||
let saved_current_story = resolution
|
||||
.saved_current_story
|
||||
.take()
|
||||
.unwrap_or_else(|| build_current_story(story_text.as_str(), &options));
|
||||
|
||||
append_story_history(
|
||||
&mut game_state,
|
||||
@@ -132,6 +118,37 @@ pub fn resolve_story_runtime_action(
|
||||
.and_then(|battle| battle.outcome.as_deref()),
|
||||
);
|
||||
|
||||
if let Some(post_battle) = finalize_post_battle_resolution(
|
||||
&mut game_state,
|
||||
history_result_text.as_str(),
|
||||
resolution
|
||||
.battle
|
||||
.as_ref()
|
||||
.and_then(|battle| battle.outcome.as_deref()),
|
||||
Vec::new(),
|
||||
) {
|
||||
resolution.story_text = Some(post_battle.story_text);
|
||||
resolution.presentation_options = Some(post_battle.presentation_options);
|
||||
resolution.saved_current_story = Some(post_battle.saved_current_story);
|
||||
}
|
||||
|
||||
let mut options = resolution
|
||||
.presentation_options
|
||||
.take()
|
||||
.unwrap_or_else(|| build_fallback_runtime_story_options(&game_state));
|
||||
if options.is_empty() {
|
||||
options = build_fallback_runtime_story_options(&game_state);
|
||||
}
|
||||
|
||||
let story_text = resolution
|
||||
.story_text
|
||||
.clone()
|
||||
.unwrap_or_else(|| resolution.result_text.clone());
|
||||
let saved_current_story = resolution
|
||||
.saved_current_story
|
||||
.take()
|
||||
.unwrap_or_else(|| build_current_story(story_text.as_str(), &options));
|
||||
|
||||
let mut patches = vec![RuntimeStoryPatch::StoryHistoryAppend {
|
||||
action_text: resolution.action_text.clone(),
|
||||
result_text: history_result_text.clone(),
|
||||
@@ -212,11 +229,10 @@ fn resolve_runtime_story_choice_action(
|
||||
resolve_action_text("主动出声试探", request),
|
||||
"你的喊话打破了当前静场,周围潜着的动静也更难继续藏住。",
|
||||
)),
|
||||
"idle_explore_forward" => Ok(simple_story_resolution(
|
||||
game_state,
|
||||
resolve_action_text("继续向前探索", request),
|
||||
"你没有停在原地,而是继续向前压,把下一段遭遇主动推到自己面前。",
|
||||
)),
|
||||
"idle_explore_forward" => resolve_idle_explore_forward_action(game_state, request),
|
||||
"idle_travel_next_scene" | "camp_travel_home_scene" => {
|
||||
resolve_idle_travel_next_scene_action(game_state, request)
|
||||
}
|
||||
"idle_observe_signs" => Ok(simple_story_resolution(
|
||||
game_state,
|
||||
resolve_action_text("观察周围迹象", request),
|
||||
@@ -309,6 +325,62 @@ fn resolve_continue_adventure_action(
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_idle_explore_forward_action(
|
||||
game_state: &mut Value,
|
||||
request: &RuntimeStoryActionRequest,
|
||||
) -> Result<StoryResolution, String> {
|
||||
// 中文注释:探索前进是战后继续链路的一环,必须在后端清掉战斗态并生成下一段遭遇预览。
|
||||
// 前端只播放表现动画,不能只靠本地状态把同一组 idle 选项重新展示一遍。
|
||||
clear_post_battle_state(game_state);
|
||||
ensure_scene_encounter_preview(game_state);
|
||||
Ok(StoryResolution {
|
||||
action_text: resolve_action_text("继续向前探索", request),
|
||||
result_text: "你没有停在原地,而是继续向前压,把下一段遭遇主动推到自己面前。".to_string(),
|
||||
story_text: None,
|
||||
presentation_options: Some(resolve_post_battle_story_options(game_state)),
|
||||
saved_current_story: None,
|
||||
patches: vec![build_status_patch(game_state)],
|
||||
battle: None,
|
||||
toast: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_idle_travel_next_scene_action(
|
||||
game_state: &mut Value,
|
||||
request: &RuntimeStoryActionRequest,
|
||||
) -> Result<StoryResolution, String> {
|
||||
// 中文注释:切场景会改变 currentScenePreset、章节 act 状态和运行统计,
|
||||
// 这些都是 runtime 快照真相,不能只在前端播放退场/进场动画。
|
||||
let payload = request.action.payload.as_ref();
|
||||
let target_scene_id = payload
|
||||
.and_then(|payload| read_optional_string_field(payload, "targetSceneId"))
|
||||
.or_else(|| resolve_forward_scene_id(game_state))
|
||||
.ok_or_else(|| "idle_travel_next_scene 缺少 targetSceneId".to_string())?;
|
||||
let next_scene = resolve_runtime_scene_preset(game_state, target_scene_id.as_str())
|
||||
.ok_or_else(|| format!("未找到目标场景:{target_scene_id}"))?;
|
||||
let next_scene_name =
|
||||
read_optional_string_field(&next_scene, "name").unwrap_or_else(|| target_scene_id.clone());
|
||||
|
||||
clear_post_battle_state(game_state);
|
||||
ensure_json_object(game_state).insert("currentScenePreset".to_string(), next_scene);
|
||||
write_i32_field(game_state, "playerX", 0);
|
||||
write_string_field(game_state, "playerFacing", "right");
|
||||
ensure_scene_act_state(game_state);
|
||||
ensure_scene_encounter_preview(game_state);
|
||||
increment_runtime_stat_local(game_state, "scenesTraveled", 1);
|
||||
|
||||
Ok(StoryResolution {
|
||||
action_text: resolve_action_text(&format!("前往{next_scene_name}"), request),
|
||||
result_text: format!("你离开当前区域,抵达了{next_scene_name}。"),
|
||||
story_text: None,
|
||||
presentation_options: Some(resolve_post_battle_story_options(game_state)),
|
||||
saved_current_story: None,
|
||||
patches: vec![build_status_patch(game_state)],
|
||||
battle: None,
|
||||
toast: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_npc_preview_talk_action(
|
||||
game_state: &mut Value,
|
||||
request: &RuntimeStoryActionRequest,
|
||||
|
||||
Reference in New Issue
Block a user